Commit 894b533b authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-dispute-mon: Add additional labels to dispute game agreement metrics (#9616)

Makes it easy to query things like the number of games with valid output roots, or the number of games that resolved incorrectly
parent abb34d44
package metrics
import (
"fmt"
"io"
"github.com/ethereum-optimism/optimism/op-service/sources/caching"
......@@ -15,12 +16,28 @@ import (
const Namespace = "op_dispute_mon"
type GameAgreementStatus uint8
const (
// In progress
AgreeChallengerAhead GameAgreementStatus = iota
DisagreeChallengerAhead
AgreeDefenderAhead
DisagreeDefenderAhead
// Completed
AgreeDefenderWins
DisagreeDefenderWins
AgreeChallengerWins
DisagreeChallengerWins
)
type Metricer interface {
RecordInfo(version string)
RecordUp()
RecordGamesStatus(inProgress, defenderWon, challengerWon int)
RecordGameAgreement(status string, count int)
RecordGameAgreement(status GameAgreementStatus, count int)
caching.Metrics
}
......@@ -84,6 +101,9 @@ func NewMetrics() *Metrics {
Help: "Number of games broken down by whether the result agrees with the reference node",
}, []string{
"status",
"completion",
"result_correctness",
"root_agreement",
}),
}
}
......@@ -122,6 +142,52 @@ func (m *Metrics) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
m.trackedGames.WithLabelValues("challenger_won").Set(float64(challengerWon))
}
func (m *Metrics) RecordGameAgreement(status string, count int) {
m.gamesAgreement.WithLabelValues(status).Set(float64(count))
func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) {
m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count))
}
const (
inProgress = true
correct = true
agree = true
)
func labelValuesFor(status GameAgreementStatus) []string {
asStrings := func(status string, inProgress bool, correct bool, agree bool) []string {
inProgressStr := "in_progress"
if !inProgress {
inProgressStr = "complete"
}
correctStr := "correct"
if !correct {
correctStr = "incorrect"
}
agreeStr := "agree"
if !agree {
agreeStr = "disagree"
}
return []string{status, inProgressStr, correctStr, agreeStr}
}
switch status {
case AgreeChallengerAhead:
return asStrings("agree_challenger_ahead", inProgress, !correct, agree)
case DisagreeChallengerAhead:
return asStrings("disagree_challenger_ahead", inProgress, correct, !agree)
case AgreeDefenderAhead:
return asStrings("agree_defender_ahead", inProgress, correct, agree)
case DisagreeDefenderAhead:
return asStrings("disagree_defender_ahead", inProgress, !correct, !agree)
// Completed
case AgreeDefenderWins:
return asStrings("agree_defender_wins", !inProgress, correct, agree)
case DisagreeDefenderWins:
return asStrings("disagree_defender_wins", !inProgress, !correct, !agree)
case AgreeChallengerWins:
return asStrings("agree_challenger_wins", !inProgress, !correct, agree)
case DisagreeChallengerWins:
return asStrings("disagree_challenger_wins", !inProgress, correct, !agree)
default:
panic(fmt.Errorf("unknown game agreement status: %v", status))
}
}
......@@ -11,4 +11,4 @@ func (*NoopMetricsImpl) CacheAdd(_ string, _ int, _ bool) {}
func (*NoopMetricsImpl) CacheGet(_ string, _ bool) {}
func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {}
func (*NoopMetricsImpl) RecordGameAgreement(status string, count int) {}
func (*NoopMetricsImpl) RecordGameAgreement(status GameAgreementStatus, count int) {}
......@@ -4,6 +4,7 @@ import (
"context"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/metrics"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/extract"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
......@@ -20,7 +21,7 @@ type GameCallerCreator interface {
}
type DetectorMetrics interface {
RecordGameAgreement(status string, count int)
RecordGameAgreement(status metrics.GameAgreementStatus, count int)
RecordGamesStatus(inProgress, defenderWon, challengerWon int)
}
......@@ -56,11 +57,10 @@ func (d *detector) Detect(ctx context.Context, games []monTypes.EnrichedGameData
}
func (d *detector) recordBatch(batch monTypes.DetectionBatch) {
d.metrics.RecordGameAgreement("in_progress", batch.InProgress)
d.metrics.RecordGameAgreement("agree_defender_wins", batch.AgreeDefenderWins)
d.metrics.RecordGameAgreement("disagree_defender_wins", batch.DisagreeDefenderWins)
d.metrics.RecordGameAgreement("agree_challenger_wins", batch.AgreeChallengerWins)
d.metrics.RecordGameAgreement("disagree_challenger_wins", batch.DisagreeChallengerWins)
d.metrics.RecordGameAgreement(metrics.AgreeDefenderWins, batch.AgreeDefenderWins)
d.metrics.RecordGameAgreement(metrics.DisagreeDefenderWins, batch.DisagreeDefenderWins)
d.metrics.RecordGameAgreement(metrics.AgreeChallengerWins, batch.AgreeChallengerWins)
d.metrics.RecordGameAgreement(metrics.DisagreeChallengerWins, batch.DisagreeChallengerWins)
}
func (d *detector) checkAgreement(ctx context.Context, addr common.Address, blockNum uint64, rootClaim common.Hash, status types.GameStatus) (monTypes.DetectionBatch, error) {
......
......@@ -6,6 +6,7 @@ import (
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/metrics"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
......@@ -17,32 +18,36 @@ func TestDetector_Detect(t *testing.T) {
t.Parallel()
t.Run("NoGames", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t)
detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{})
metrics.Equals(t, 0, 0, 0)
metrics.Mapped(t, map[string]int{})
m.Equals(t, 0, 0, 0)
m.Mapped(t, map[metrics.GameAgreementStatus]int{})
})
t.Run("CheckAgreementFails", func(t *testing.T) {
detector, metrics, rollup, _ := setupDetectorTest(t)
detector, m, rollup, _ := setupDetectorTest(t)
rollup.err = errors.New("boom")
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}})
metrics.Equals(t, 1, 0, 0) // Status should still be metriced here!
metrics.Mapped(t, map[string]int{})
m.Equals(t, 1, 0, 0) // Status should still be metriced here!
m.Mapped(t, map[metrics.GameAgreementStatus]int{})
})
t.Run("SingleGame", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}})
metrics.Equals(t, 1, 0, 0)
metrics.Mapped(t, map[string]int{"in_progress": 1})
detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{Status: types.GameStatusChallengerWon}})
m.Equals(t, 0, 0, 1)
m.Mapped(t, map[metrics.GameAgreementStatus]int{metrics.DisagreeChallengerWins: 1})
})
t.Run("MultipleGames", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}, {}, {}})
metrics.Equals(t, 3, 0, 0)
metrics.Mapped(t, map[string]int{"in_progress": 3})
detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{
{Status: types.GameStatusChallengerWon},
{Status: types.GameStatusChallengerWon},
{Status: types.GameStatusChallengerWon},
})
m.Equals(t, 0, 0, 3)
m.Mapped(t, map[metrics.GameAgreementStatus]int{metrics.DisagreeChallengerWins: 3})
})
}
......@@ -60,36 +65,38 @@ func TestDetector_RecordBatch(t *testing.T) {
{
name: "in_progress",
batch: monTypes.DetectionBatch{InProgress: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["in_progress"])
expect: func(t *testing.T, m *mockDetectorMetricer) {
for status, count := range m.gameAgreement {
require.Zerof(t, count, "incorrectly reported in progress game as %v", status)
}
},
},
{
name: "agree_defender_wins",
batch: monTypes.DetectionBatch{AgreeDefenderWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["agree_defender_wins"])
expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, m.gameAgreement[metrics.AgreeDefenderWins])
},
},
{
name: "disagree_defender_wins",
batch: monTypes.DetectionBatch{DisagreeDefenderWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["disagree_defender_wins"])
expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, m.gameAgreement[metrics.DisagreeDefenderWins])
},
},
{
name: "agree_challenger_wins",
batch: monTypes.DetectionBatch{AgreeChallengerWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["agree_challenger_wins"])
expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, m.gameAgreement[metrics.AgreeChallengerWins])
},
},
{
name: "disagree_challenger_wins",
batch: monTypes.DetectionBatch{DisagreeChallengerWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["disagree_challenger_wins"])
expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, m.gameAgreement[metrics.DisagreeChallengerWins])
},
},
}
......@@ -214,7 +221,7 @@ type mockDetectorMetricer struct {
inProgress int
defenderWon int
challengerWon int
gameAgreement map[string]int
gameAgreement map[metrics.GameAgreementStatus]int
}
func (m *mockDetectorMetricer) Equals(t *testing.T, inProgress, defenderWon, challengerWon int) {
......@@ -223,7 +230,7 @@ func (m *mockDetectorMetricer) Equals(t *testing.T, inProgress, defenderWon, cha
require.Equal(t, challengerWon, m.challengerWon)
}
func (m *mockDetectorMetricer) Mapped(t *testing.T, expected map[string]int) {
func (m *mockDetectorMetricer) Mapped(t *testing.T, expected map[metrics.GameAgreementStatus]int) {
for k, v := range m.gameAgreement {
require.Equal(t, expected[k], v)
}
......@@ -235,9 +242,9 @@ func (m *mockDetectorMetricer) RecordGamesStatus(inProgress, defenderWon, challe
m.challengerWon = challengerWon
}
func (m *mockDetectorMetricer) RecordGameAgreement(status string, count int) {
func (m *mockDetectorMetricer) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
if m.gameAgreement == nil {
m.gameAgreement = make(map[string]int)
m.gameAgreement = make(map[metrics.GameAgreementStatus]int)
}
m.gameAgreement[status] += count
}
......@@ -6,6 +6,7 @@ import (
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/metrics"
"github.com/ethereum-optimism/optimism/op-dispute-mon/mon/transform"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
......@@ -20,7 +21,7 @@ var (
)
type ForecastMetrics interface {
RecordGameAgreement(status string, count int)
RecordGameAgreement(status metrics.GameAgreementStatus, count int)
}
type forecast struct {
......@@ -48,10 +49,10 @@ func (f *forecast) Forecast(ctx context.Context, games []monTypes.EnrichedGameDa
}
func (f *forecast) recordBatch(batch monTypes.ForecastBatch) {
f.metrics.RecordGameAgreement("agree_challenger_ahead", batch.AgreeChallengerAhead)
f.metrics.RecordGameAgreement("disagree_challenger_ahead", batch.DisagreeChallengerAhead)
f.metrics.RecordGameAgreement("agree_defender_ahead", batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement("disagree_defender_ahead", batch.DisagreeDefenderAhead)
f.metrics.RecordGameAgreement(metrics.AgreeChallengerAhead, batch.AgreeChallengerAhead)
f.metrics.RecordGameAgreement(metrics.DisagreeChallengerAhead, batch.DisagreeChallengerAhead)
f.metrics.RecordGameAgreement(metrics.AgreeDefenderAhead, batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement(metrics.DisagreeDefenderAhead, batch.DisagreeDefenderAhead)
}
func (f *forecast) forecastGame(ctx context.Context, game monTypes.EnrichedGameData, metrics *monTypes.ForecastBatch) error {
......
......@@ -8,6 +8,7 @@ import (
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/metrics"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
......@@ -266,15 +267,15 @@ type mockForecastMetrics struct {
disagreeChallengerAhead int
}
func (m *mockForecastMetrics) RecordGameAgreement(status string, count int) {
func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
switch status {
case "agree_defender_ahead":
case metrics.AgreeDefenderAhead:
m.agreeDefenderAhead = count
case "disagree_defender_ahead":
case metrics.DisagreeDefenderAhead:
m.disagreeDefenderAhead = count
case "agree_challenger_ahead":
case metrics.AgreeChallengerAhead:
m.agreeChallengerAhead = count
case "disagree_challenger_ahead":
case metrics.DisagreeChallengerAhead:
m.disagreeChallengerAhead = count
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment