diff --git a/dot/state/mock_gauge_test.go b/dot/state/mock_gauge_test.go new file mode 100644 index 00000000000..0cf4274d1ba --- /dev/null +++ b/dot/state/mock_gauge_test.go @@ -0,0 +1,160 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prometheus/client_golang/prometheus (interfaces: Gauge) + +// Package state is a generated GoMock package. +package state + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + prometheus "github.com/prometheus/client_golang/prometheus" + io_prometheus_client "github.com/prometheus/client_model/go" +) + +// MockGauge is a mock of Gauge interface. +type MockGauge struct { + ctrl *gomock.Controller + recorder *MockGaugeMockRecorder +} + +// MockGaugeMockRecorder is the mock recorder for MockGauge. +type MockGaugeMockRecorder struct { + mock *MockGauge +} + +// NewMockGauge creates a new mock instance. +func NewMockGauge(ctrl *gomock.Controller) *MockGauge { + mock := &MockGauge{ctrl: ctrl} + mock.recorder = &MockGaugeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGauge) EXPECT() *MockGaugeMockRecorder { + return m.recorder +} + +// Add mocks base method. +func (m *MockGauge) Add(arg0 float64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Add", arg0) +} + +// Add indicates an expected call of Add. +func (mr *MockGaugeMockRecorder) Add(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockGauge)(nil).Add), arg0) +} + +// Collect mocks base method. +func (m *MockGauge) Collect(arg0 chan<- prometheus.Metric) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Collect", arg0) +} + +// Collect indicates an expected call of Collect. +func (mr *MockGaugeMockRecorder) Collect(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collect", reflect.TypeOf((*MockGauge)(nil).Collect), arg0) +} + +// Dec mocks base method. +func (m *MockGauge) Dec() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Dec") +} + +// Dec indicates an expected call of Dec. +func (mr *MockGaugeMockRecorder) Dec() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dec", reflect.TypeOf((*MockGauge)(nil).Dec)) +} + +// Desc mocks base method. +func (m *MockGauge) Desc() *prometheus.Desc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Desc") + ret0, _ := ret[0].(*prometheus.Desc) + return ret0 +} + +// Desc indicates an expected call of Desc. +func (mr *MockGaugeMockRecorder) Desc() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Desc", reflect.TypeOf((*MockGauge)(nil).Desc)) +} + +// Describe mocks base method. +func (m *MockGauge) Describe(arg0 chan<- *prometheus.Desc) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Describe", arg0) +} + +// Describe indicates an expected call of Describe. +func (mr *MockGaugeMockRecorder) Describe(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Describe", reflect.TypeOf((*MockGauge)(nil).Describe), arg0) +} + +// Inc mocks base method. +func (m *MockGauge) Inc() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Inc") +} + +// Inc indicates an expected call of Inc. +func (mr *MockGaugeMockRecorder) Inc() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Inc", reflect.TypeOf((*MockGauge)(nil).Inc)) +} + +// Set mocks base method. +func (m *MockGauge) Set(arg0 float64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Set", arg0) +} + +// Set indicates an expected call of Set. +func (mr *MockGaugeMockRecorder) Set(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockGauge)(nil).Set), arg0) +} + +// SetToCurrentTime mocks base method. +func (m *MockGauge) SetToCurrentTime() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetToCurrentTime") +} + +// SetToCurrentTime indicates an expected call of SetToCurrentTime. +func (mr *MockGaugeMockRecorder) SetToCurrentTime() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToCurrentTime", reflect.TypeOf((*MockGauge)(nil).SetToCurrentTime)) +} + +// Sub mocks base method. +func (m *MockGauge) Sub(arg0 float64) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Sub", arg0) +} + +// Sub indicates an expected call of Sub. +func (mr *MockGaugeMockRecorder) Sub(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sub", reflect.TypeOf((*MockGauge)(nil).Sub), arg0) +} + +// Write mocks base method. +func (m *MockGauge) Write(arg0 *io_prometheus_client.Metric) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Write", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Write indicates an expected call of Write. +func (mr *MockGaugeMockRecorder) Write(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockGauge)(nil).Write), arg0) +} diff --git a/dot/state/storage.go b/dot/state/storage.go index 94772187df0..0dfe84f4708 100644 --- a/dot/state/storage.go +++ b/dot/state/storage.go @@ -43,7 +43,8 @@ type StorageState struct { // NewStorageState creates a new StorageState backed by the given trie and database located at basePath. func NewStorageState(db chaindb.Database, blockState *BlockState, - t *trie.Trie, onlinePruner pruner.Config) (*StorageState, error) { + t *trie.Trie, onlinePruner pruner.Config) ( + *StorageState, error) { if db == nil { return nil, fmt.Errorf("cannot have nil database") } @@ -52,7 +53,10 @@ func NewStorageState(db chaindb.Database, blockState *BlockState, return nil, fmt.Errorf("cannot have nil trie") } - tries := newTries(t) + tries, err := newTries(t) + if err != nil { + return nil, fmt.Errorf("cannot create in-memory tries: %w", err) + } storageTable := chaindb.NewTable(db, storagePrefix) diff --git a/dot/state/tries.go b/dot/state/tries.go index e7afd3dbb15..b14b0a15c4d 100644 --- a/dot/state/tries.go +++ b/dot/state/tries.go @@ -4,23 +4,38 @@ package state import ( + "errors" + "fmt" "sync" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/trie" + "github.com/prometheus/client_golang/prometheus" ) type tries struct { rootToTrie map[common.Hash]*trie.Trie mapMutex sync.RWMutex + triesGauge prometheus.Gauge } -func newTries(t *trie.Trie) *tries { +func newTries(t *trie.Trie) (trs *tries, err error) { + triesGauge := prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "gossamer_storage", + Name: "tries_cached_total", + Help: "total number of tries cached in memory", + }) + err = prometheus.Register(triesGauge) + if err != nil && !errors.As(err, &prometheus.AlreadyRegisteredError{}) { + return nil, fmt.Errorf("cannot register tries gauge: %w", err) + } + return &tries{ rootToTrie: map[common.Hash]*trie.Trie{ t.MustHash(): t, }, - } + triesGauge: triesGauge, + }, nil } // softSet sets the given trie at the given root hash @@ -34,6 +49,7 @@ func (t *tries) softSet(root common.Hash, trie *trie.Trie) { return } + t.triesGauge.Inc() t.rootToTrie[root] = trie } @@ -41,6 +57,7 @@ func (t *tries) delete(root common.Hash) { t.mapMutex.Lock() defer t.mapMutex.Unlock() delete(t.rootToTrie, root) + t.triesGauge.Set(float64(len(t.rootToTrie))) } // get retrieves the trie corresponding to the root hash given diff --git a/dot/state/tries_test.go b/dot/state/tries_test.go index 0a0bc1d8652..3e1f594225f 100644 --- a/dot/state/tries_test.go +++ b/dot/state/tries_test.go @@ -9,7 +9,10 @@ import ( "github.com/ChainSafe/gossamer/internal/trie/node" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/trie" + "github.com/golang/mock/gomock" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_newTries(t *testing.T) { @@ -17,50 +20,52 @@ func Test_newTries(t *testing.T) { tr := trie.NewEmptyTrie() - rootToTrie := newTries(tr) + rootToTrie, err := newTries(tr) + require.NoError(t, err) expectedTries := &tries{ rootToTrie: map[common.Hash]*trie.Trie{ tr.MustHash(): tr, }, + triesGauge: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "gossamer_storage", + Name: "tries_cached_total", + Help: "total number of tries cached in memory", + }), } assert.Equal(t, expectedTries, rootToTrie) } +//go:generate mockgen -destination=mock_gauge_test.go -package $GOPACKAGE github.com/prometheus/client_golang/prometheus Gauge + func Test_tries_softSet(t *testing.T) { t.Parallel() testCases := map[string]struct { - tries *tries - root common.Hash - trie *trie.Trie - expectedTries *tries + rootToTrie map[common.Hash]*trie.Trie + root common.Hash + trie *trie.Trie + triesGaugeInc bool + expectedRootToTrie map[common.Hash]*trie.Trie }{ "set new in map": { - tries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{}, - }, - root: common.Hash{1, 2, 3}, - trie: trie.NewEmptyTrie(), - expectedTries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{ - {1, 2, 3}: trie.NewEmptyTrie(), - }, + rootToTrie: map[common.Hash]*trie.Trie{}, + root: common.Hash{1, 2, 3}, + trie: trie.NewEmptyTrie(), + triesGaugeInc: true, + expectedRootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: trie.NewEmptyTrie(), }, }, "do not override in map": { - tries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{ - {1, 2, 3}: {}, - }, + rootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: {}, }, root: common.Hash{1, 2, 3}, trie: trie.NewEmptyTrie(), - expectedTries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{ - {1, 2, 3}: {}, - }, + expectedRootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: {}, }, }, } @@ -69,10 +74,21 @@ func Test_tries_softSet(t *testing.T) { testCase := testCase t.Run(name, func(t *testing.T) { t.Parallel() + ctrl := gomock.NewController(t) - testCase.tries.softSet(testCase.root, testCase.trie) + triesGauge := NewMockGauge(ctrl) + if testCase.triesGaugeInc { + triesGauge.EXPECT().Inc() + } - assert.Equal(t, testCase.expectedTries, testCase.tries) + tries := &tries{ + rootToTrie: testCase.rootToTrie, + triesGauge: triesGauge, + } + + tries.softSet(testCase.root, testCase.trie) + + assert.Equal(t, testCase.expectedRootToTrie, tries.rootToTrie) }) } } @@ -81,29 +97,27 @@ func Test_tries_delete(t *testing.T) { t.Parallel() testCases := map[string]struct { - tries *tries - root common.Hash - expectedTries *tries + rootToTrie map[common.Hash]*trie.Trie + root common.Hash + triesGaugeSet float64 + expectedRootToTrie map[common.Hash]*trie.Trie }{ "not found": { - tries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{}, + rootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: {}, }, - root: common.Hash{1, 2, 3}, - expectedTries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{}, + root: common.Hash{9}, + triesGaugeSet: 1, + expectedRootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: {}, }, }, "deleted": { - tries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{ - {1, 2, 3}: {}, - }, - }, - root: common.Hash{1, 2, 3}, - expectedTries: &tries{ - rootToTrie: map[common.Hash]*trie.Trie{}, + rootToTrie: map[common.Hash]*trie.Trie{ + {1, 2, 3}: {}, }, + root: common.Hash{1, 2, 3}, + expectedRootToTrie: map[common.Hash]*trie.Trie{}, }, } @@ -111,10 +125,19 @@ func Test_tries_delete(t *testing.T) { testCase := testCase t.Run(name, func(t *testing.T) { t.Parallel() + ctrl := gomock.NewController(t) + + triesGauge := NewMockGauge(ctrl) + triesGauge.EXPECT().Set(testCase.triesGaugeSet) + + tries := &tries{ + rootToTrie: testCase.rootToTrie, + triesGauge: triesGauge, + } - testCase.tries.delete(testCase.root) + tries.delete(testCase.root) - assert.Equal(t, testCase.expectedTries, testCase.tries) + assert.Equal(t, testCase.expectedRootToTrie, tries.rootToTrie) }) } }