diff --git a/dapps/valuetransfers/fpc.go b/dapps/valuetransfers/fpc.go index 3c96930f2ec94e9e2b4d0bfdea71454ed60d5a11..7706260d7646a38abb980f3db9d10d1257179874 100644 --- a/dapps/valuetransfers/fpc.go +++ b/dapps/valuetransfers/fpc.go @@ -3,13 +3,13 @@ package valuetransfers import ( "context" "fmt" - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/metrics" "net" "strconv" "sync" + "github.com/golang/protobuf/proto" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager" + "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/packages/prng" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/vote" @@ -185,7 +185,10 @@ func (pog *PeerOpinionGiver) Query(ctx context.Context, ids []string) (vote.Opin query := &votenet.QueryRequest{Id: ids} reply, err := client.Opinion(ctx, query) if err != nil { - // TODO: add an event QueryErrorEvent (metrics) + metrics.Events().QueryReplyError.Trigger(&metrics.QueryReplyErrorEvent{ + ID: pog.p.ID().String(), + OpinionCount: len(ids), + }) return nil, fmt.Errorf("unable to query opinions: %w", err) } diff --git a/packages/metrics/events.go b/packages/metrics/events.go index d5e5ff06301cff0a42b69caba6c09dc4ee647177..5da40e84b33f4db8feeb076b8a98040c061ae1df 100644 --- a/packages/metrics/events.go +++ b/packages/metrics/events.go @@ -13,6 +13,27 @@ type CollectionEvents struct { Synced *events.Event ValueTips *events.Event MessageTips *events.Event + QueryReceived *events.Event + QueryReplyError *events.Event +} + +// QueryReceivedEvent is used to pass information through a QueryReceived event. +type QueryReceivedEvent struct { + OpinionCount int +} + +// QueryReplyErrorEvent is used to pass information through a QueryReplyError event. +type QueryReplyErrorEvent struct { + ID string + OpinionCount int +} + +func queryReceivedEventCaller(handler interface{}, params ...interface{}) { + handler.(func(ev *QueryReceivedEvent))(params[0].(*QueryReceivedEvent)) +} + +func queryReplyErrorEventCaller(handler interface{}, params ...interface{}) { + handler.(func(ev *QueryReplyErrorEvent))(params[0].(*QueryReplyErrorEvent)) } func uint64Caller(handler interface{}, params ...interface{}) { diff --git a/packages/metrics/metrics.go b/packages/metrics/metrics.go index 0f3797760d9947f296555886f5fa71a4f0d874dd..cb5dfb8a26c8c05488035492c9b9305aa666ad47 100644 --- a/packages/metrics/metrics.go +++ b/packages/metrics/metrics.go @@ -20,6 +20,8 @@ func new() *CollectionEvents { events.NewEvent(boolCaller), events.NewEvent(uint64Caller), events.NewEvent(uint64Caller), + events.NewEvent(queryReceivedEventCaller), + events.NewEvent(queryReplyErrorEventCaller), } } diff --git a/packages/vote/net/server.go b/packages/vote/net/server.go index 3e9c621a963b8f2abc526d0d0e85614ed3b53078..6d4c99e28fbe3fdfab2f2ab2c5c1c82f6f635541 100644 --- a/packages/vote/net/server.go +++ b/packages/vote/net/server.go @@ -2,10 +2,10 @@ package net import ( "context" - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/metrics" "net" + "github.com/golang/protobuf/proto" + "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/packages/vote" "google.golang.org/grpc" ) @@ -46,7 +46,7 @@ func (vs *VoterServer) Opinion(ctx context.Context, req *QueryRequest) (*QueryRe reply.Opinion[i] = int32(vs.opnRetriever(id)) } - // trigger metrics event + metrics.Events().QueryReceived.Trigger(&metrics.QueryReceivedEvent{OpinionCount: len(req.Id)}) metrics.Events().FPCInboundBytes.Trigger(proto.Size(req)) metrics.Events().FPCOutboundBytes.Trigger(proto.Size(reply)) return reply, nil diff --git a/plugins/metrics/fpc.go b/plugins/metrics/fpc.go index eb97c82e1c3ce712da6c35238122d555dff0a24e..544c39cff1594fab93e9a7ea0a280539d55bc025 100644 --- a/plugins/metrics/fpc.go +++ b/plugins/metrics/fpc.go @@ -3,9 +3,9 @@ package metrics import ( "sync/atomic" - "github.com/iotaledger/hive.go/syncutils" - + "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/packages/vote" + "github.com/iotaledger/hive.go/syncutils" ) var activeConflicts uint64 @@ -14,6 +14,18 @@ var failedConflictCount uint64 var averageRoundsToFinalize float64 var avLock syncutils.RWMutex +// QueryReceivedCount is the number of queries received (each query can contain multiple conflicts to give an opinion about) +var QueryReceivedCount uint64 + +// OpinionQueryReceivedCount is the number of opinion queries received (multiple in one query) +var OpinionQueryReceivedCount uint64 + +// QueryReplyErrorCount counts how many times we haven't received an answer for our query. (each query reply can contain multiple conflicts to get an opinion about) +var QueryReplyErrorCount uint64 + +// OpinionQueryReplyErrorCount counts how many opinions we asked for but never heard back (multiple opinions in one query) +var OpinionQueryReplyErrorCount uint64 + // ActiveConflicts returns the number of currently active conflicts. func ActiveConflicts() uint64 { return atomic.LoadUint64(&activeConflicts) @@ -36,6 +48,26 @@ func AverageRoundsToFinalize() float64 { return averageRoundsToFinalize } +// FPCQueryReceived returns the number of received voting queries. For an exact number of opinion queries, use FPCOpinionQueryReceived(). +func FPCQueryReceived() uint64 { + return atomic.LoadUint64(&QueryReceivedCount) +} + +// FPCOpinionQueryReceived returns the number of received opinion queries. +func FPCOpinionQueryReceived() uint64 { + return atomic.LoadUint64(&OpinionQueryReceivedCount) +} + +// FPCQueryReplyErrors returns the number of sent but unanswered queries for conflict opinions. For an exact number of failed opinions, use FPCOpinionQueryReplyErrors(). +func FPCQueryReplyErrors() uint64 { + return atomic.LoadUint64(&QueryReplyErrorCount) +} + +// FPCOpinionQueryReplyErrors returns the number of opinions that the node failed to gather from peers. +func FPCOpinionQueryReplyErrors() uint64 { + return atomic.LoadUint64(&OpinionQueryReplyErrorCount) +} + //// logic broken into "process..." functions to be able to write unit tests //// func processRoundStats(stats vote.RoundStats) { @@ -58,3 +90,17 @@ func processFinalized(ctx vote.Context) { func processFailed(ctx vote.Context) { atomic.AddUint64(&failedConflictCount, 1) } + +func processQueryReceived(ev *metrics.QueryReceivedEvent) { + // received one query + atomic.AddUint64(&QueryReceivedCount, 1) + // containing this many conflicts to give opinion about + atomic.AddUint64(&OpinionQueryReceivedCount, (uint64)(ev.OpinionCount)) +} + +func processQueryReplyError(ev *metrics.QueryReplyErrorEvent) { + // received one query + atomic.AddUint64(&QueryReplyErrorCount, 1) + // containing this many conflicts to give opinion about + atomic.AddUint64(&OpinionQueryReplyErrorCount, (uint64)(ev.OpinionCount)) +} diff --git a/plugins/metrics/fpc_test.go b/plugins/metrics/fpc_test.go index 27627062f14d3a2643f5c561c9db82d4a41cadd8..4281c10697bc6e20792a283cd2c13a291cabea09 100644 --- a/plugins/metrics/fpc_test.go +++ b/plugins/metrics/fpc_test.go @@ -3,8 +3,8 @@ package metrics import ( "testing" + "github.com/iotaledger/goshimmer/packages/metrics" "github.com/iotaledger/goshimmer/packages/vote" - "github.com/magiconair/properties/assert" ) @@ -57,3 +57,33 @@ func TestFinalize(t *testing.T) { // => average should be 7.5 assert.Equal(t, AverageRoundsToFinalize(), 7.5) } + +func TestQueryReceived(t *testing.T) { + assert.Equal(t, FPCQueryReceived(), (uint64)(0)) + assert.Equal(t, FPCOpinionQueryReceived(), (uint64)(0)) + + processQueryReceived(&metrics.QueryReceivedEvent{OpinionCount: 5}) + + assert.Equal(t, FPCQueryReceived(), (uint64)(1)) + assert.Equal(t, FPCOpinionQueryReceived(), (uint64)(5)) + + processQueryReceived(&metrics.QueryReceivedEvent{OpinionCount: 5}) + + assert.Equal(t, FPCQueryReceived(), (uint64)(2)) + assert.Equal(t, FPCOpinionQueryReceived(), (uint64)(10)) +} + +func TestQueryReplyError(t *testing.T) { + assert.Equal(t, FPCQueryReplyErrors(), (uint64)(0)) + assert.Equal(t, FPCOpinionQueryReplyErrors(), (uint64)(0)) + + processQueryReplyError(&metrics.QueryReplyErrorEvent{OpinionCount: 5}) + + assert.Equal(t, FPCQueryReplyErrors(), (uint64)(1)) + assert.Equal(t, FPCOpinionQueryReplyErrors(), (uint64)(5)) + + processQueryReplyError(&metrics.QueryReplyErrorEvent{OpinionCount: 5}) + + assert.Equal(t, FPCQueryReplyErrors(), (uint64)(2)) + assert.Equal(t, FPCOpinionQueryReplyErrors(), (uint64)(10)) +} diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go index c6c4a360e6c36ac0ecc54288d911d04917f206da..7d2f4a21cbc56b147b0fef216241f56f5ff8cdd7 100644 --- a/plugins/metrics/plugin.go +++ b/plugins/metrics/plugin.go @@ -92,6 +92,13 @@ func configure(_ *node.Plugin) { metrics.Events().ValueTips.Attach(events.NewClosure(func(tipsCount uint64) { atomic.StoreUint64(&valueTips, tipsCount) })) + + metrics.Events().QueryReceived.Attach(events.NewClosure(func(ev *metrics.QueryReceivedEvent) { + processQueryReceived(ev) + })) + metrics.Events().QueryReplyError.Attach(events.NewClosure(func(ev *metrics.QueryReplyErrorEvent) { + processQueryReplyError(ev) + })) } func run(_ *node.Plugin) {