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 package metrics
import ( import (
"fmt"
"io" "io"
"github.com/ethereum-optimism/optimism/op-service/sources/caching" "github.com/ethereum-optimism/optimism/op-service/sources/caching"
...@@ -15,12 +16,28 @@ import ( ...@@ -15,12 +16,28 @@ import (
const Namespace = "op_dispute_mon" 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 { type Metricer interface {
RecordInfo(version string) RecordInfo(version string)
RecordUp() RecordUp()
RecordGamesStatus(inProgress, defenderWon, challengerWon int) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
RecordGameAgreement(status string, count int) RecordGameAgreement(status GameAgreementStatus, count int)
caching.Metrics caching.Metrics
} }
...@@ -84,6 +101,9 @@ func NewMetrics() *Metrics { ...@@ -84,6 +101,9 @@ func NewMetrics() *Metrics {
Help: "Number of games broken down by whether the result agrees with the reference node", Help: "Number of games broken down by whether the result agrees with the reference node",
}, []string{ }, []string{
"status", "status",
"completion",
"result_correctness",
"root_agreement",
}), }),
} }
} }
...@@ -122,6 +142,52 @@ func (m *Metrics) RecordGamesStatus(inProgress, defenderWon, challengerWon int) ...@@ -122,6 +142,52 @@ func (m *Metrics) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
m.trackedGames.WithLabelValues("challenger_won").Set(float64(challengerWon)) m.trackedGames.WithLabelValues("challenger_won").Set(float64(challengerWon))
} }
func (m *Metrics) RecordGameAgreement(status string, count int) { func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) {
m.gamesAgreement.WithLabelValues(status).Set(float64(count)) 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) {} ...@@ -11,4 +11,4 @@ func (*NoopMetricsImpl) CacheAdd(_ string, _ int, _ bool) {}
func (*NoopMetricsImpl) CacheGet(_ string, _ bool) {} func (*NoopMetricsImpl) CacheGet(_ string, _ bool) {}
func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {} 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 ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "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" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/extract"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
...@@ -20,7 +21,7 @@ type GameCallerCreator interface { ...@@ -20,7 +21,7 @@ type GameCallerCreator interface {
} }
type DetectorMetrics interface { type DetectorMetrics interface {
RecordGameAgreement(status string, count int) RecordGameAgreement(status metrics.GameAgreementStatus, count int)
RecordGamesStatus(inProgress, defenderWon, challengerWon int) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
} }
...@@ -56,11 +57,10 @@ func (d *detector) Detect(ctx context.Context, games []monTypes.EnrichedGameData ...@@ -56,11 +57,10 @@ func (d *detector) Detect(ctx context.Context, games []monTypes.EnrichedGameData
} }
func (d *detector) recordBatch(batch monTypes.DetectionBatch) { func (d *detector) recordBatch(batch monTypes.DetectionBatch) {
d.metrics.RecordGameAgreement("in_progress", batch.InProgress) d.metrics.RecordGameAgreement(metrics.AgreeDefenderWins, batch.AgreeDefenderWins)
d.metrics.RecordGameAgreement("agree_defender_wins", batch.AgreeDefenderWins) d.metrics.RecordGameAgreement(metrics.DisagreeDefenderWins, batch.DisagreeDefenderWins)
d.metrics.RecordGameAgreement("disagree_defender_wins", batch.DisagreeDefenderWins) d.metrics.RecordGameAgreement(metrics.AgreeChallengerWins, batch.AgreeChallengerWins)
d.metrics.RecordGameAgreement("agree_challenger_wins", batch.AgreeChallengerWins) d.metrics.RecordGameAgreement(metrics.DisagreeChallengerWins, batch.DisagreeChallengerWins)
d.metrics.RecordGameAgreement("disagree_challenger_wins", batch.DisagreeChallengerWins)
} }
func (d *detector) checkAgreement(ctx context.Context, addr common.Address, blockNum uint64, rootClaim common.Hash, status types.GameStatus) (monTypes.DetectionBatch, error) { 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 ( ...@@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/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" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -17,32 +18,36 @@ func TestDetector_Detect(t *testing.T) { ...@@ -17,32 +18,36 @@ func TestDetector_Detect(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("NoGames", func(t *testing.T) { t.Run("NoGames", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t) detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{}) detector.Detect(context.Background(), []monTypes.EnrichedGameData{})
metrics.Equals(t, 0, 0, 0) m.Equals(t, 0, 0, 0)
metrics.Mapped(t, map[string]int{}) m.Mapped(t, map[metrics.GameAgreementStatus]int{})
}) })
t.Run("CheckAgreementFails", func(t *testing.T) { t.Run("CheckAgreementFails", func(t *testing.T) {
detector, metrics, rollup, _ := setupDetectorTest(t) detector, m, rollup, _ := setupDetectorTest(t)
rollup.err = errors.New("boom") rollup.err = errors.New("boom")
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}}) detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}})
metrics.Equals(t, 1, 0, 0) // Status should still be metriced here! m.Equals(t, 1, 0, 0) // Status should still be metriced here!
metrics.Mapped(t, map[string]int{}) m.Mapped(t, map[metrics.GameAgreementStatus]int{})
}) })
t.Run("SingleGame", func(t *testing.T) { t.Run("SingleGame", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t) detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}}) detector.Detect(context.Background(), []monTypes.EnrichedGameData{{Status: types.GameStatusChallengerWon}})
metrics.Equals(t, 1, 0, 0) m.Equals(t, 0, 0, 1)
metrics.Mapped(t, map[string]int{"in_progress": 1}) m.Mapped(t, map[metrics.GameAgreementStatus]int{metrics.DisagreeChallengerWins: 1})
}) })
t.Run("MultipleGames", func(t *testing.T) { t.Run("MultipleGames", func(t *testing.T) {
detector, metrics, _, _ := setupDetectorTest(t) detector, m, _, _ := setupDetectorTest(t)
detector.Detect(context.Background(), []monTypes.EnrichedGameData{{}, {}, {}}) detector.Detect(context.Background(), []monTypes.EnrichedGameData{
metrics.Equals(t, 3, 0, 0) {Status: types.GameStatusChallengerWon},
metrics.Mapped(t, map[string]int{"in_progress": 3}) {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) { ...@@ -60,36 +65,38 @@ func TestDetector_RecordBatch(t *testing.T) {
{ {
name: "in_progress", name: "in_progress",
batch: monTypes.DetectionBatch{InProgress: 1}, batch: monTypes.DetectionBatch{InProgress: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) { expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["in_progress"]) for status, count := range m.gameAgreement {
require.Zerof(t, count, "incorrectly reported in progress game as %v", status)
}
}, },
}, },
{ {
name: "agree_defender_wins", name: "agree_defender_wins",
batch: monTypes.DetectionBatch{AgreeDefenderWins: 1}, batch: monTypes.DetectionBatch{AgreeDefenderWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) { expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["agree_defender_wins"]) require.Equal(t, 1, m.gameAgreement[metrics.AgreeDefenderWins])
}, },
}, },
{ {
name: "disagree_defender_wins", name: "disagree_defender_wins",
batch: monTypes.DetectionBatch{DisagreeDefenderWins: 1}, batch: monTypes.DetectionBatch{DisagreeDefenderWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) { expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["disagree_defender_wins"]) require.Equal(t, 1, m.gameAgreement[metrics.DisagreeDefenderWins])
}, },
}, },
{ {
name: "agree_challenger_wins", name: "agree_challenger_wins",
batch: monTypes.DetectionBatch{AgreeChallengerWins: 1}, batch: monTypes.DetectionBatch{AgreeChallengerWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) { expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["agree_challenger_wins"]) require.Equal(t, 1, m.gameAgreement[metrics.AgreeChallengerWins])
}, },
}, },
{ {
name: "disagree_challenger_wins", name: "disagree_challenger_wins",
batch: monTypes.DetectionBatch{DisagreeChallengerWins: 1}, batch: monTypes.DetectionBatch{DisagreeChallengerWins: 1},
expect: func(t *testing.T, metrics *mockDetectorMetricer) { expect: func(t *testing.T, m *mockDetectorMetricer) {
require.Equal(t, 1, metrics.gameAgreement["disagree_challenger_wins"]) require.Equal(t, 1, m.gameAgreement[metrics.DisagreeChallengerWins])
}, },
}, },
} }
...@@ -214,7 +221,7 @@ type mockDetectorMetricer struct { ...@@ -214,7 +221,7 @@ type mockDetectorMetricer struct {
inProgress int inProgress int
defenderWon int defenderWon int
challengerWon int challengerWon int
gameAgreement map[string]int gameAgreement map[metrics.GameAgreementStatus]int
} }
func (m *mockDetectorMetricer) Equals(t *testing.T, inProgress, defenderWon, challengerWon 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 ...@@ -223,7 +230,7 @@ func (m *mockDetectorMetricer) Equals(t *testing.T, inProgress, defenderWon, cha
require.Equal(t, challengerWon, m.challengerWon) 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 { for k, v := range m.gameAgreement {
require.Equal(t, expected[k], v) require.Equal(t, expected[k], v)
} }
...@@ -235,9 +242,9 @@ func (m *mockDetectorMetricer) RecordGamesStatus(inProgress, defenderWon, challe ...@@ -235,9 +242,9 @@ func (m *mockDetectorMetricer) RecordGamesStatus(inProgress, defenderWon, challe
m.challengerWon = challengerWon m.challengerWon = challengerWon
} }
func (m *mockDetectorMetricer) RecordGameAgreement(status string, count int) { func (m *mockDetectorMetricer) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
if m.gameAgreement == nil { if m.gameAgreement == nil {
m.gameAgreement = make(map[string]int) m.gameAgreement = make(map[metrics.GameAgreementStatus]int)
} }
m.gameAgreement[status] += count m.gameAgreement[status] += count
} }
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "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" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/transform"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
...@@ -20,7 +21,7 @@ var ( ...@@ -20,7 +21,7 @@ var (
) )
type ForecastMetrics interface { type ForecastMetrics interface {
RecordGameAgreement(status string, count int) RecordGameAgreement(status metrics.GameAgreementStatus, count int)
} }
type forecast struct { type forecast struct {
...@@ -48,10 +49,10 @@ func (f *forecast) Forecast(ctx context.Context, games []monTypes.EnrichedGameDa ...@@ -48,10 +49,10 @@ func (f *forecast) Forecast(ctx context.Context, games []monTypes.EnrichedGameDa
} }
func (f *forecast) recordBatch(batch monTypes.ForecastBatch) { func (f *forecast) recordBatch(batch monTypes.ForecastBatch) {
f.metrics.RecordGameAgreement("agree_challenger_ahead", batch.AgreeChallengerAhead) f.metrics.RecordGameAgreement(metrics.AgreeChallengerAhead, batch.AgreeChallengerAhead)
f.metrics.RecordGameAgreement("disagree_challenger_ahead", batch.DisagreeChallengerAhead) f.metrics.RecordGameAgreement(metrics.DisagreeChallengerAhead, batch.DisagreeChallengerAhead)
f.metrics.RecordGameAgreement("agree_defender_ahead", batch.AgreeDefenderAhead) f.metrics.RecordGameAgreement(metrics.AgreeDefenderAhead, batch.AgreeDefenderAhead)
f.metrics.RecordGameAgreement("disagree_defender_ahead", batch.DisagreeDefenderAhead) f.metrics.RecordGameAgreement(metrics.DisagreeDefenderAhead, batch.DisagreeDefenderAhead)
} }
func (f *forecast) forecastGame(ctx context.Context, game monTypes.EnrichedGameData, metrics *monTypes.ForecastBatch) error { func (f *forecast) forecastGame(ctx context.Context, game monTypes.EnrichedGameData, metrics *monTypes.ForecastBatch) error {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" 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-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-dispute-mon/metrics"
monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -266,15 +267,15 @@ type mockForecastMetrics struct { ...@@ -266,15 +267,15 @@ type mockForecastMetrics struct {
disagreeChallengerAhead int disagreeChallengerAhead int
} }
func (m *mockForecastMetrics) RecordGameAgreement(status string, count int) { func (m *mockForecastMetrics) RecordGameAgreement(status metrics.GameAgreementStatus, count int) {
switch status { switch status {
case "agree_defender_ahead": case metrics.AgreeDefenderAhead:
m.agreeDefenderAhead = count m.agreeDefenderAhead = count
case "disagree_defender_ahead": case metrics.DisagreeDefenderAhead:
m.disagreeDefenderAhead = count m.disagreeDefenderAhead = count
case "agree_challenger_ahead": case metrics.AgreeChallengerAhead:
m.agreeChallengerAhead = count m.agreeChallengerAhead = count
case "disagree_challenger_ahead": case metrics.DisagreeChallengerAhead:
m.disagreeChallengerAhead = count 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