Commit 6096d317 authored by refcell's avatar refcell Committed by GitHub

feat(op-dispute-mon): output fetch time metric (#9744)

parent 5928a728
......@@ -38,6 +38,8 @@ type Metricer interface {
RecordClaimResolutionDelayMax(delay float64)
RecordOutputFetchTime(timestamp float64)
RecordGamesStatus(inProgress, defenderWon, challengerWon int)
RecordGameAgreement(status GameAgreementStatus, count int)
......@@ -57,6 +59,8 @@ type Metrics struct {
info prometheus.GaugeVec
up prometheus.Gauge
lastOutputFetch prometheus.Gauge
claimResolutionDelayMax prometheus.Gauge
trackedGames prometheus.GaugeVec
......@@ -92,6 +96,11 @@ func NewMetrics() *Metrics {
Name: "up",
Help: "1 if the op-challenger has finished starting up",
}),
lastOutputFetch: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "last_output_fetch",
Help: "Timestamp of the last output fetch",
}),
claimResolutionDelayMax: factory.NewGauge(prometheus.GaugeOpts{
Namespace: Namespace,
Name: "claim_resolution_delay_max",
......@@ -155,6 +164,10 @@ func (m *Metrics) RecordGamesStatus(inProgress, defenderWon, challengerWon int)
m.trackedGames.WithLabelValues("challenger_won").Set(float64(challengerWon))
}
func (m *Metrics) RecordOutputFetchTime(timestamp float64) {
m.lastOutputFetch.Set(timestamp)
}
func (m *Metrics) RecordGameAgreement(status GameAgreementStatus, count int) {
m.gamesAgreement.WithLabelValues(labelValuesFor(status)...).Set(float64(count))
}
......
......@@ -12,5 +12,7 @@ func (*NoopMetricsImpl) CacheGet(_ string, _ bool) {}
func (*NoopMetricsImpl) RecordClaimResolutionDelayMax(delay float64) {}
func (*NoopMetricsImpl) RecordOutputFetchTime(timestamp float64) {}
func (*NoopMetricsImpl) RecordGamesStatus(inProgress, defenderWon, challengerWon int) {}
func (*NoopMetricsImpl) RecordGameAgreement(status GameAgreementStatus, count int) {}
......@@ -100,7 +100,7 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error
}
func (s *Service) initOutputValidator() {
s.validator = newOutputValidator(s.rollupClient)
s.validator = newOutputValidator(s.metrics, s.rollupClient)
}
func (s *Service) initGameCallerCreator() {
......
......@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/ethereum/go-ethereum/common"
......@@ -14,13 +15,19 @@ type OutputRollupClient interface {
OutputAtBlock(ctx context.Context, blockNum uint64) (*eth.OutputResponse, error)
}
type OutputMetrics interface {
RecordOutputFetchTime(float64)
}
type outputValidator struct {
client OutputRollupClient
metrics OutputMetrics
client OutputRollupClient
}
func newOutputValidator(client OutputRollupClient) *outputValidator {
func newOutputValidator(metrics OutputMetrics, client OutputRollupClient) *outputValidator {
return &outputValidator{
client: client,
metrics: metrics,
client: client,
}
}
......@@ -35,6 +42,7 @@ func (o *outputValidator) CheckRootAgreement(ctx context.Context, blockNum uint6
}
return false, common.Hash{}, fmt.Errorf("failed to get output at block: %w", err)
}
o.metrics.RecordOutputFetchTime(float64(time.Now().Unix()))
expected := common.Hash(output.OutputRoot)
return rootClaim == expected, expected, nil
}
......@@ -18,45 +18,58 @@ func TestDetector_CheckRootAgreement(t *testing.T) {
t.Parallel()
t.Run("OutputFetchFails", func(t *testing.T) {
validator, rollup := setupOutputValidatorTest(t)
validator, rollup, metrics := setupOutputValidatorTest(t)
rollup.err = errors.New("boom")
agree, fetched, err := validator.CheckRootAgreement(context.Background(), 0, mockRootClaim)
require.ErrorIs(t, err, rollup.err)
require.Equal(t, common.Hash{}, fetched)
require.False(t, agree)
require.Zero(t, metrics.fetchTime)
})
t.Run("OutputMismatch", func(t *testing.T) {
validator, _ := setupOutputValidatorTest(t)
validator, _, metrics := setupOutputValidatorTest(t)
agree, fetched, err := validator.CheckRootAgreement(context.Background(), 0, common.Hash{})
require.NoError(t, err)
require.Equal(t, mockRootClaim, fetched)
require.False(t, agree)
require.NotZero(t, metrics.fetchTime)
})
t.Run("OutputMatches", func(t *testing.T) {
validator, _ := setupOutputValidatorTest(t)
validator, _, metrics := setupOutputValidatorTest(t)
agree, fetched, err := validator.CheckRootAgreement(context.Background(), 0, mockRootClaim)
require.NoError(t, err)
require.Equal(t, mockRootClaim, fetched)
require.True(t, agree)
require.NotZero(t, metrics.fetchTime)
})
t.Run("OutputNotFound", func(t *testing.T) {
validator, rollup := setupOutputValidatorTest(t)
validator, rollup, metrics := setupOutputValidatorTest(t)
// This crazy error is what we actually get back from the API
rollup.err = errors.New("failed to get L2 block ref with sync status: failed to determine L2BlockRef of height 42984924, could not get payload: not found")
agree, fetched, err := validator.CheckRootAgreement(context.Background(), 42984924, mockRootClaim)
require.NoError(t, err)
require.Equal(t, common.Hash{}, fetched)
require.False(t, agree)
require.Zero(t, metrics.fetchTime)
})
}
func setupOutputValidatorTest(t *testing.T) (*outputValidator, *stubRollupClient) {
func setupOutputValidatorTest(t *testing.T) (*outputValidator, *stubRollupClient, *stubOutputMetrics) {
client := &stubRollupClient{}
validator := newOutputValidator(client)
return validator, client
metrics := &stubOutputMetrics{}
validator := newOutputValidator(metrics, client)
return validator, client, metrics
}
type stubOutputMetrics struct {
fetchTime float64
}
func (s *stubOutputMetrics) RecordOutputFetchTime(fetchTime float64) {
s.fetchTime = fetchTime
}
type stubRollupClient struct {
......
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