Skip to content

Commit a1045b2

Browse files
authored
Move validator metadata DB writes into validatorState (#5008)
1 parent c1f29fc commit a1045b2

File tree

3 files changed

+232
-54
lines changed

3 files changed

+232
-54
lines changed

vms/platformvm/state/metadata_validator.go

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,15 @@ func parseValidatorMetadata(bytes []byte, metadata *validatorMetadata) error {
7777

7878
type validatorState struct {
7979
metadata map[ids.NodeID]map[ids.ID]*validatorMetadata // vdrID -> subnetID -> metadata
80-
// updatedMetadata tracks the updates since WriteValidatorMetadata was last called
81-
updatedMetadata map[ids.NodeID]set.Set[ids.ID] // vdrID -> subnetIDs
80+
// updatedMetadata tracks (vdrID, subnetID) -> txIDs needing DB sync since the
81+
// last WriteValidatorMetadata.
82+
updatedMetadata map[ids.NodeID]map[ids.ID]set.Set[ids.ID]
8283
}
8384

8485
func newValidatorState() *validatorState {
8586
return &validatorState{
8687
metadata: make(map[ids.NodeID]map[ids.ID]*validatorMetadata),
87-
updatedMetadata: make(map[ids.NodeID]set.Set[ids.ID]),
88+
updatedMetadata: make(map[ids.NodeID]map[ids.ID]set.Set[ids.ID]),
8889
}
8990
}
9091

@@ -105,6 +106,17 @@ func (vs *validatorState) LoadValidatorMetadata(
105106
subnetMetadata[subnetID] = uptime
106107
}
107108

109+
// AddValidatorMetadata loads the metadata and marks it as updated so it will
110+
// be written to disk on the next call to [WriteValidatorMetadata].
111+
func (vs *validatorState) AddValidatorMetadata(
112+
vdrID ids.NodeID,
113+
subnetID ids.ID,
114+
vm *validatorMetadata,
115+
) {
116+
vs.LoadValidatorMetadata(vdrID, subnetID, vm)
117+
vs.addUpdatedTxID(vdrID, subnetID, vm.txID)
118+
}
119+
108120
// GetUptime returns the current uptime measurements of `vdrID` on
109121
// `subnetID`.
110122
func (vs *validatorState) GetUptime(
@@ -134,7 +146,7 @@ func (vs *validatorState) SetUptime(
134146
metadata.UpDuration = upDuration
135147
metadata.lastUpdated = lastUpdated
136148

137-
vs.addUpdatedMetadata(vdrID, subnetID)
149+
vs.addUpdatedTxID(vdrID, subnetID, metadata.txID)
138150
return nil
139151
}
140152

@@ -165,62 +177,75 @@ func (vs *validatorState) SetDelegateeReward(
165177
}
166178
metadata.PotentialDelegateeReward = amount
167179

168-
vs.addUpdatedMetadata(vdrID, subnetID)
180+
vs.addUpdatedTxID(vdrID, subnetID, metadata.txID)
169181
return nil
170182
}
171183

172184
// DeleteValidatorMetadata removes in-memory references to the metadata of
173-
// `vdrID` on `subnetID`. If there were staged updates from a prior call to
174-
// [SetUptime] or [SetDelegateeReward], the updates will be dropped. This call
175-
// will not result in a write to disk.
185+
// `vdrID` on `subnetID`. The txID is recorded for deletion from disk on the
186+
// next [WriteValidatorMetadata]. Any staged updates from [SetUptime] or
187+
// [SetDelegateeReward] are dropped.
176188
func (vs *validatorState) DeleteValidatorMetadata(vdrID ids.NodeID, subnetID ids.ID) {
177189
subnetMetadata := vs.metadata[vdrID]
190+
md, exists := subnetMetadata[subnetID]
191+
if exists {
192+
vs.addUpdatedTxID(vdrID, subnetID, md.txID)
193+
}
194+
178195
delete(subnetMetadata, subnetID)
179196
if len(subnetMetadata) == 0 {
180197
delete(vs.metadata, vdrID)
181198
}
182-
183-
subnetUpdatedMetadata := vs.updatedMetadata[vdrID]
184-
subnetUpdatedMetadata.Remove(subnetID)
185-
if subnetUpdatedMetadata.Len() == 0 {
186-
delete(vs.updatedMetadata, vdrID)
187-
}
188199
}
189200

190-
// WriteValidatorMetadata writes all staged updates from prior calls to
191-
// [SetUptime] or [SetDelegateeReward].
201+
// WriteValidatorMetadata persists all entries in updatedMetadata to disk. For
202+
// each (vdrID, subnetID) and txID in the set: if metadata exists and its txID
203+
// matches, write it to disk; otherwise delete the txID from disk.
192204
func (vs *validatorState) WriteValidatorMetadata(
193-
dbPrimary database.KeyValueWriter,
194-
dbSubnet database.KeyValueWriter,
205+
dbPrimary database.KeyValueWriterDeleter,
206+
dbSubnet database.KeyValueWriterDeleter,
195207
codecVersion uint16,
196208
) error {
197-
for vdrID, updatedSubnets := range vs.updatedMetadata {
198-
for subnetID := range updatedSubnets {
199-
metadata := vs.metadata[vdrID][subnetID]
200-
metadata.LastUpdated = uint64(metadata.lastUpdated.Unix())
201-
202-
metadataBytes, err := MetadataCodec.Marshal(codecVersion, metadata)
203-
if err != nil {
204-
return err
205-
}
209+
for vdrID, bySubnet := range vs.updatedMetadata {
210+
for subnetID, txIDs := range bySubnet {
206211
db := dbSubnet
207212
if subnetID == constants.PrimaryNetworkID {
208213
db = dbPrimary
209214
}
210-
if err := db.Put(metadata.txID[:], metadataBytes); err != nil {
211-
return err
215+
216+
metadata, hasMetadata := vs.metadata[vdrID][subnetID]
217+
for txID := range txIDs {
218+
if !hasMetadata || txID != metadata.txID {
219+
if err := db.Delete(txID[:]); err != nil {
220+
return err
221+
}
222+
} else {
223+
metadata.LastUpdated = uint64(metadata.lastUpdated.Unix())
224+
metadataBytes, err := MetadataCodec.Marshal(codecVersion, metadata)
225+
if err != nil {
226+
return err
227+
}
228+
if err := db.Put(metadata.txID[:], metadataBytes); err != nil {
229+
return err
230+
}
231+
}
212232
}
213233
}
214-
delete(vs.updatedMetadata, vdrID)
215234
}
235+
vs.updatedMetadata = make(map[ids.NodeID]map[ids.ID]set.Set[ids.ID])
216236
return nil
217237
}
218238

219-
func (vs *validatorState) addUpdatedMetadata(vdrID ids.NodeID, subnetID ids.ID) {
220-
updatedSubnetMetadata, ok := vs.updatedMetadata[vdrID]
239+
func (vs *validatorState) addUpdatedTxID(vdrID ids.NodeID, subnetID ids.ID, txID ids.ID) {
240+
subnet, ok := vs.updatedMetadata[vdrID]
241+
if !ok {
242+
subnet = make(map[ids.ID]set.Set[ids.ID])
243+
vs.updatedMetadata[vdrID] = subnet
244+
}
245+
txIDs, ok := subnet[subnetID]
221246
if !ok {
222-
updatedSubnetMetadata = set.Set[ids.ID]{}
223-
vs.updatedMetadata[vdrID] = updatedSubnetMetadata
247+
txIDs = set.Set[ids.ID]{}
248+
subnet[subnetID] = txIDs
224249
}
225-
updatedSubnetMetadata.Add(subnetID)
250+
txIDs.Add(txID)
226251
}

vms/platformvm/state/metadata_validator_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/ava-labs/avalanchego/database"
1414
"github.com/ava-labs/avalanchego/database/memdb"
1515
"github.com/ava-labs/avalanchego/ids"
16+
"github.com/ava-labs/avalanchego/utils/constants"
1617
"github.com/ava-labs/avalanchego/utils/wrappers"
1718
)
1819

@@ -171,6 +172,167 @@ func TestValidatorDelegateeRewards(t *testing.T) {
171172
require.ErrorIs(err, database.ErrNotFound)
172173
}
173174

175+
func TestAddValidatorMetadataWrite(t *testing.T) {
176+
tests := []struct {
177+
name string
178+
subnetID ids.ID
179+
wantPrimary bool
180+
}{
181+
{
182+
name: "primary network",
183+
subnetID: constants.PrimaryNetworkID,
184+
wantPrimary: true,
185+
},
186+
{
187+
name: "subnet",
188+
subnetID: ids.GenerateTestID(),
189+
wantPrimary: false,
190+
},
191+
}
192+
193+
for _, tt := range tests {
194+
t.Run(tt.name, func(t *testing.T) {
195+
require := require.New(t)
196+
state := newValidatorState()
197+
primaryDB := memdb.New()
198+
subnetDB := memdb.New()
199+
200+
txID := ids.GenerateTestID()
201+
state.AddValidatorMetadata(ids.GenerateTestNodeID(), tt.subnetID, &validatorMetadata{
202+
txID: txID,
203+
PotentialReward: 100,
204+
})
205+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
206+
207+
hasPrimary, err := primaryDB.Has(txID[:])
208+
require.NoError(err)
209+
hasSubnet, err := subnetDB.Has(txID[:])
210+
require.NoError(err)
211+
212+
require.Equal(tt.wantPrimary, hasPrimary)
213+
require.Equal(!tt.wantPrimary, hasSubnet)
214+
})
215+
}
216+
}
217+
218+
func TestDeleteValidatorMetadataWrite(t *testing.T) {
219+
require := require.New(t)
220+
state := newValidatorState()
221+
primaryDB := memdb.New()
222+
subnetDB := memdb.New()
223+
224+
nodeID := ids.GenerateTestNodeID()
225+
txID := ids.GenerateTestID()
226+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
227+
txID: txID,
228+
PotentialReward: 100,
229+
})
230+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
231+
232+
state.DeleteValidatorMetadata(nodeID, constants.PrimaryNetworkID)
233+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
234+
235+
has, err := primaryDB.Has(txID[:])
236+
require.NoError(err)
237+
require.False(has)
238+
}
239+
240+
func TestAddThenDeleteValidatorMetadataWrite(t *testing.T) {
241+
require := require.New(t)
242+
state := newValidatorState()
243+
primaryDB := memdb.New()
244+
subnetDB := memdb.New()
245+
246+
nodeID := ids.GenerateTestNodeID()
247+
txID := ids.GenerateTestID()
248+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
249+
txID: txID,
250+
PotentialReward: 100,
251+
})
252+
state.DeleteValidatorMetadata(nodeID, constants.PrimaryNetworkID)
253+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
254+
255+
has, err := primaryDB.Has(txID[:])
256+
require.NoError(err)
257+
require.False(has)
258+
}
259+
260+
func TestDeleteThenReAddValidatorMetadataWrite(t *testing.T) {
261+
require := require.New(t)
262+
state := newValidatorState()
263+
primaryDB := memdb.New()
264+
subnetDB := memdb.New()
265+
266+
nodeID := ids.GenerateTestNodeID()
267+
oldTxID := ids.GenerateTestID()
268+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
269+
txID: oldTxID,
270+
PotentialReward: 100,
271+
})
272+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
273+
274+
state.DeleteValidatorMetadata(nodeID, constants.PrimaryNetworkID)
275+
newTxID := ids.GenerateTestID()
276+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
277+
txID: newTxID,
278+
PotentialReward: 200,
279+
})
280+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
281+
282+
has, err := primaryDB.Has(oldTxID[:])
283+
require.NoError(err)
284+
require.False(has)
285+
286+
has, err = primaryDB.Has(newTxID[:])
287+
require.NoError(err)
288+
require.True(has)
289+
}
290+
291+
func TestDeleteAddDeleteAddValidatorMetadataWrite(t *testing.T) {
292+
require := require.New(t)
293+
state := newValidatorState()
294+
primaryDB := memdb.New()
295+
subnetDB := memdb.New()
296+
297+
nodeID := ids.GenerateTestNodeID()
298+
txID1 := ids.GenerateTestID()
299+
txID2 := ids.GenerateTestID()
300+
txID3 := ids.GenerateTestID()
301+
302+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
303+
txID: txID1,
304+
PotentialReward: 100,
305+
})
306+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
307+
has, err := primaryDB.Has(txID1[:])
308+
require.NoError(err)
309+
require.True(has)
310+
311+
state.DeleteValidatorMetadata(nodeID, constants.PrimaryNetworkID)
312+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
313+
txID: txID2,
314+
PotentialReward: 200,
315+
})
316+
state.DeleteValidatorMetadata(nodeID, constants.PrimaryNetworkID)
317+
state.AddValidatorMetadata(nodeID, constants.PrimaryNetworkID, &validatorMetadata{
318+
txID: txID3,
319+
PotentialReward: 300,
320+
})
321+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, CodecVersion1))
322+
323+
has, err = primaryDB.Has(txID1[:])
324+
require.NoError(err)
325+
require.False(has)
326+
327+
has, err = primaryDB.Has(txID2[:])
328+
require.NoError(err)
329+
require.False(has)
330+
331+
has, err = primaryDB.Has(txID3[:])
332+
require.NoError(err)
333+
require.True(has)
334+
}
335+
174336
func TestParseValidatorMetadata(t *testing.T) {
175337
type test struct {
176338
name string

vms/platformvm/state/state.go

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,7 +2256,6 @@ func (s *state) write(updateValidators bool, height uint64) error {
22562256
s.writeValidatorDiffs(height),
22572257
s.writeCurrentStakers(codecVersion),
22582258
s.writePendingStakers(),
2259-
s.validatorState.WriteValidatorMetadata(s.currentValidatorList, s.currentSubnetValidatorList, codecVersion), // Must be called after writeCurrentStakers
22602259
s.writeL1Validators(),
22612260
s.writeTXs(),
22622261
s.writeRewardUTXOs(),
@@ -2804,15 +2803,11 @@ func getOrSetDefault[K comparable, V any](m map[K]*V, k K) *V {
28042803

28052804
func (s *state) writeCurrentStakers(codecVersion uint16) error {
28062805
for subnetID, validatorDiffs := range s.currentStakers.validatorDiffs {
2807-
// Select db to write to
2808-
validatorDB := s.currentSubnetValidatorList
28092806
delegatorDB := s.currentSubnetDelegatorList
28102807
if subnetID == constants.PrimaryNetworkID {
2811-
validatorDB = s.currentValidatorList
28122808
delegatorDB = s.currentDelegatorList
28132809
}
28142810

2815-
// Record the change in weight and/or public key for each validator.
28162811
for nodeID, validatorDiff := range validatorDiffs {
28172812
switch validatorDiff.validatorStatus {
28182813
case added:
@@ -2834,21 +2829,8 @@ func (s *state) writeCurrentStakers(codecVersion uint16) error {
28342829
PotentialDelegateeReward: 0,
28352830
}
28362831

2837-
metadataBytes, err := MetadataCodec.Marshal(codecVersion, metadata)
2838-
if err != nil {
2839-
return fmt.Errorf("failed to serialize current validator: %w", err)
2840-
}
2841-
2842-
if err = validatorDB.Put(staker.TxID[:], metadataBytes); err != nil {
2843-
return fmt.Errorf("failed to write current validator to list: %w", err)
2844-
}
2845-
2846-
s.validatorState.LoadValidatorMetadata(nodeID, subnetID, metadata)
2832+
s.validatorState.AddValidatorMetadata(nodeID, subnetID, metadata)
28472833
case deleted:
2848-
if err := validatorDB.Delete(validatorDiff.validator.TxID[:]); err != nil {
2849-
return fmt.Errorf("failed to delete current staker: %w", err)
2850-
}
2851-
28522834
s.validatorState.DeleteValidatorMetadata(nodeID, subnetID)
28532835
}
28542836

@@ -2862,6 +2844,15 @@ func (s *state) writeCurrentStakers(codecVersion uint16) error {
28622844
}
28632845
}
28642846
}
2847+
2848+
if err := s.validatorState.WriteValidatorMetadata(
2849+
s.currentValidatorList,
2850+
s.currentSubnetValidatorList,
2851+
codecVersion,
2852+
); err != nil {
2853+
return err
2854+
}
2855+
28652856
maps.Clear(s.currentStakers.validatorDiffs)
28662857
return nil
28672858
}

0 commit comments

Comments
 (0)