diff --git a/packages/metrics/events.go b/packages/metrics/events.go index 727c35f1cb95ab932be9ecbe125d6c2ae3ce19d2..067cc2b3eadbae0440bd7c2ea824cd5f339965ca 100644 --- a/packages/metrics/events.go +++ b/packages/metrics/events.go @@ -1,6 +1,7 @@ package metrics import ( + "github.com/iotaledger/goshimmer/packages/vote" "github.com/iotaledger/hive.go/events" ) @@ -17,6 +18,7 @@ type CollectionEvents struct { MessageTips *events.Event QueryReceived *events.Event QueryReplyError *events.Event + AnalysisFPCFinalized *events.Event } // QueryReceivedEvent is used to pass information through a QueryReceived event. @@ -30,6 +32,16 @@ type QueryReplyErrorEvent struct { OpinionCount int } +// AnalysisFPCFinalizedEvent is triggered by the analysis-server to +// notify a finalized FPC vote from one node. +type AnalysisFPCFinalizedEvent struct { + ConflictID string + NodeID string + Rounds int + Opinions []vote.Opinion + Status vote.Opinion +} + func queryReceivedEventCaller(handler interface{}, params ...interface{}) { handler.(func(ev *QueryReceivedEvent))(params[0].(*QueryReceivedEvent)) } @@ -49,3 +61,7 @@ func float64Caller(handler interface{}, params ...interface{}) { func boolCaller(handler interface{}, params ...interface{}) { handler.(func(bool))(params[0].(bool)) } + +func fpcFinalizedEventCaller(handler interface{}, params ...interface{}) { + handler.(func(ev *AnalysisFPCFinalizedEvent))(params[0].(*AnalysisFPCFinalizedEvent)) +} diff --git a/packages/metrics/metrics.go b/packages/metrics/metrics.go index b6e40aea55f8b08b7e1aea5982995479e84cb929..01c67730e4779f6b1eeceb6198f75a7140dcb093 100644 --- a/packages/metrics/metrics.go +++ b/packages/metrics/metrics.go @@ -24,6 +24,7 @@ func new() *CollectionEvents { MessageTips: events.NewEvent(uint64Caller), QueryReceived: events.NewEvent(queryReceivedEventCaller), QueryReplyError: events.NewEvent(queryReplyErrorEventCaller), + AnalysisFPCFinalized: events.NewEvent(fpcFinalizedEventCaller), } } diff --git a/packages/vote/opinion.go b/packages/vote/opinion.go index 7ae144665db24c58c68a2b4c342666a2358505ca..7a87c0284448bbfb4707bf364e92a7ebfd179bf0 100644 --- a/packages/vote/opinion.go +++ b/packages/vote/opinion.go @@ -51,6 +51,15 @@ func ConvertInt32Opinion(x int32) Opinion { return Unknown } +// ConvertOpinionsToInts32 converts the given slice of int32 to a slice of Opinion. +func ConvertInts32ToOpinions(opinions []int32) []Opinion { + result := make([]Opinion, len(opinions)) + for i, opinion := range opinions { + result[i] = ConvertInt32Opinion(opinion) + } + return result +} + // ConvertOpinionToInt32 converts the given Opinion to an int32. func ConvertOpinionToInt32(x Opinion) int32 { switch { diff --git a/plugins/analysis/client/fpc_heartbeato.go b/plugins/analysis/client/fpc_heartbeato.go index 582a9feebc8f6e9e8f7d10033252cdccb0a6fd0a..5a3f8e9f966390cab86d94359f658760939f2a22 100644 --- a/plugins/analysis/client/fpc_heartbeato.go +++ b/plugins/analysis/client/fpc_heartbeato.go @@ -14,9 +14,9 @@ var ( finalizedMutex sync.RWMutex ) -func onFinalized(id string, opinion vote.Opinion) { +func onFinalized(ev *vote.OpinionEvent) { finalizedMutex.Lock() - finalized[id] = opinion + finalized[ev.ID] = ev.Opinion finalizedMutex.Unlock() } diff --git a/plugins/analysis/dashboard/fpc_livefeed.go b/plugins/analysis/dashboard/fpc_livefeed.go index 42f04c2bf2b8ab1b2550f0564a1ba6121c0a1f16..7397834c8067052f4702502999db124bd761dd74 100644 --- a/plugins/analysis/dashboard/fpc_livefeed.go +++ b/plugins/analysis/dashboard/fpc_livefeed.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/vote" "github.com/iotaledger/goshimmer/plugins/analysis/packet" @@ -120,9 +121,16 @@ func createFPCUpdate(hb *packet.FPCHeartbeat, recordEvent bool) *FPCUpdate { Status: conflictDetail.Status, } i++ + + metrics.Events().AnalysisFPCFinalized.Trigger(&metrics.AnalysisFPCFinalizedEvent{ + ConflictID: ID, + NodeID: conflictDetail.NodeID, + Rounds: conflictDetail.Rounds, + Opinions: vote.ConvertInts32ToOpinions(conflictDetail.Opinions), + Status: vote.ConvertInt32Opinion(conflictDetail.Status), + }) } - //log.Info("Storing:\n", finalizedConflicts) err := storeFPCRecords(finalizedConflicts, mongoDB()) if err != nil { log.Errorf("Error while writing on MongoDB: %s", err) diff --git a/plugins/prometheus/clients_info.go b/plugins/prometheus/clients_info.go index 49841f768508444adef11819cb1d00e581ea2f44..c4b27e754b4565451dae7068242fc6359c6fc324 100644 --- a/plugins/prometheus/clients_info.go +++ b/plugins/prometheus/clients_info.go @@ -3,19 +3,57 @@ package prometheus import ( "strconv" + metricspkg "github.com/iotaledger/goshimmer/packages/metrics" + "github.com/iotaledger/goshimmer/packages/vote" analysisdashboard "github.com/iotaledger/goshimmer/plugins/analysis/dashboard" "github.com/iotaledger/goshimmer/plugins/metrics" + "github.com/iotaledger/hive.go/events" "github.com/prometheus/client_golang/prometheus" ) +const ( + // LIKE conflict outcome + LIKE = "LIKE" + // DISLIKE conflict outcome + DISLIKE = "DISLIKE" +) + var ( clientsInfoCPU *prometheus.GaugeVec clientsInfoMemory *prometheus.GaugeVec clientsNeighborCount *prometheus.GaugeVec networkDiameter prometheus.Gauge + + conflictCount *prometheus.GaugeVec + conflictFinalizationRounds *prometheus.GaugeVec + conflictOutcome *prometheus.GaugeVec + conflictInitialOpinion *prometheus.GaugeVec ) +var onFPCFinalized = events.NewClosure(func(ev *metricspkg.AnalysisFPCFinalizedEvent) { + conflictCount.WithLabelValues( + ev.NodeID, + ).Add(1) + + conflictFinalizationRounds.WithLabelValues( + ev.ConflictID, + ev.NodeID, + ).Set(float64(ev.Rounds + 1)) + + conflictOutcome.WithLabelValues( + ev.ConflictID, + ev.NodeID, + opinionToString(ev.Status), + ).Set(1) + + conflictInitialOpinion.WithLabelValues( + ev.ConflictID, + ev.NodeID, + opinionToString(ev.Opinions[0]), + ).Set(1) +}) + func registerClientsMetrics() { clientsInfoCPU = prometheus.NewGaugeVec( prometheus.GaugeOpts{ @@ -46,7 +84,7 @@ func registerClientsMetrics() { clientsNeighborCount = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "clients_neighbor_count", - Help: "Info about client's memory usage labeled with nodeID, OS, ARCH and number of cpu cores", + Help: "Info about client's neighbors count", }, []string{ "nodeID", @@ -59,12 +97,64 @@ func registerClientsMetrics() { Help: "Autopeering network diameter", }) + conflictCount = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "conflict_count", + Help: "Conflicts count labeled with nodeID", + }, + []string{ + "nodeID", + }, + ) + + conflictFinalizationRounds = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "conflict_finalization_rounds", + Help: "Number of rounds to finalize a given conflict labeled with conflictID and nodeID", + }, + []string{ + "conflictID", + "nodeID", + }, + ) + + conflictInitialOpinion = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "conflict_initial_opinion", + Help: "Initial opinion of a given conflict labeled with conflictID, nodeID and opinion", + }, + []string{ + "conflictID", + "nodeID", + "opinion", + }, + ) + + conflictOutcome = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "conflict_outcome", + Help: "Outcome of a given conflict labeled with conflictID, nodeID and opinion", + }, + []string{ + "conflictID", + "nodeID", + "opinion", + }, + ) + registry.MustRegister(clientsInfoCPU) registry.MustRegister(clientsInfoMemory) registry.MustRegister(clientsNeighborCount) registry.MustRegister(networkDiameter) + registry.MustRegister(conflictFinalizationRounds) + registry.MustRegister(conflictFinalizationRounds) + registry.MustRegister(conflictInitialOpinion) + registry.MustRegister(conflictOutcome) + addCollect(collectClientsInfo) + + metricspkg.Events().AnalysisFPCFinalized.Attach(onFPCFinalized) } func collectClientsInfo() { @@ -89,5 +179,13 @@ func collectClientsInfo() { clientsNeighborCount.WithLabelValues(nodeID, "in").Set(float64(neighborCount.Inbound)) clientsNeighborCount.WithLabelValues(nodeID, "out").Set(float64(neighborCount.Inbound)) } + networkDiameter.Set(float64(metrics.NetworkDiameter())) } + +func opinionToString(opinion vote.Opinion) string { + if opinion == vote.Like { + return LIKE + } + return DISLIKE +}