-
Notifications
You must be signed in to change notification settings - Fork 176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dynamic Protocol State] TODOs and refactoring, part 2 #5080
Changes from 2 commits
cd22475
475574d
e22ad8b
d7a3cf7
41b8362
d9fb53d
815ba07
6c81ef2
337c066
f89f5eb
66b1bab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -126,7 +126,52 @@ func TestNewRichProtocolStateEntry(t *testing.T) { | |||||
assert.Equal(t, expectedIdentities, richEntry.NextEpochIdentityTable, "should be equal to next epoch setup participants + current epoch setup participants") | ||||||
}) | ||||||
|
||||||
// TODO: include test for epoch setup phase where no prior epoch exist (i.e. first epoch setup phase after spork) | ||||||
t.Run("setup-after-spork", func(t *testing.T) { | ||||||
stateEntry := unittest.ProtocolStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { | ||||||
// no previous epoch since we are in the first epoch | ||||||
entry.PreviousEpochSetup = nil | ||||||
entry.PreviousEpochCommit = nil | ||||||
entry.PreviousEpoch = nil | ||||||
|
||||||
// next epoch is setup but not committed | ||||||
entry.NextEpochCommit = nil | ||||||
entry.NextEpoch.CommitID = flow.ZeroID | ||||||
}) | ||||||
|
||||||
assert.Nil(t, stateEntry.PreviousEpoch) | ||||||
assert.Nil(t, stateEntry.PreviousEpochSetup) | ||||||
assert.Nil(t, stateEntry.PreviousEpochCommit) | ||||||
|
||||||
richEntry, err := flow.NewRichProtocolStateEntry( | ||||||
stateEntry.ProtocolStateEntry, | ||||||
stateEntry.PreviousEpochSetup, | ||||||
stateEntry.PreviousEpochCommit, | ||||||
stateEntry.CurrentEpochSetup, | ||||||
stateEntry.CurrentEpochCommit, | ||||||
stateEntry.NextEpochSetup, | ||||||
nil, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
expectedIdentities, err := flow.BuildIdentityTable( | ||||||
stateEntry.CurrentEpochSetup.Participants, | ||||||
stateEntry.CurrentEpoch.ActiveIdentities, | ||||||
stateEntry.NextEpochSetup.Participants, | ||||||
stateEntry.NextEpoch.ActiveIdentities, | ||||||
flow.EpochParticipationStatusJoining, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
assert.Equal(t, expectedIdentities, richEntry.CurrentEpochIdentityTable, "should be equal to current epoch setup participants + next epoch setup participants") | ||||||
assert.Nil(t, richEntry.NextEpochCommit) | ||||||
expectedIdentities, err = flow.BuildIdentityTable( | ||||||
stateEntry.NextEpochSetup.Participants, | ||||||
stateEntry.NextEpoch.ActiveIdentities, | ||||||
stateEntry.CurrentEpochSetup.Participants, | ||||||
stateEntry.CurrentEpoch.ActiveIdentities, | ||||||
flow.EpochParticipationStatusLeaving, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
assert.Equal(t, expectedIdentities, richEntry.NextEpochIdentityTable, "should be equal to next epoch setup participants + current epoch setup participants") | ||||||
}) | ||||||
|
||||||
// Common situation during the epoch commit phase for epoch N+1 | ||||||
// * we are currently in Epoch N | ||||||
|
@@ -165,8 +210,47 @@ func TestNewRichProtocolStateEntry(t *testing.T) { | |||||
assert.Equal(t, expectedIdentities, richEntry.NextEpochIdentityTable, "should be equal to next epoch setup participants + current epoch setup participants") | ||||||
}) | ||||||
|
||||||
// TODO: include test for epoch commit phase where no prior epoch exist (i.e. first epoch commit phase after spork) | ||||||
t.Run("commit-after-spork", func(t *testing.T) { | ||||||
stateEntry := unittest.ProtocolStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { | ||||||
// no previous epoch since we are in the first epoch | ||||||
entry.PreviousEpochSetup = nil | ||||||
entry.PreviousEpochCommit = nil | ||||||
entry.PreviousEpoch = nil | ||||||
}) | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
👉 Implemented in my PR #5116 |
||||||
assert.Nil(t, stateEntry.PreviousEpoch) | ||||||
assert.Nil(t, stateEntry.PreviousEpochSetup) | ||||||
assert.Nil(t, stateEntry.PreviousEpochCommit) | ||||||
|
||||||
richEntry, err := flow.NewRichProtocolStateEntry( | ||||||
stateEntry.ProtocolStateEntry, | ||||||
stateEntry.PreviousEpochSetup, | ||||||
stateEntry.PreviousEpochCommit, | ||||||
stateEntry.CurrentEpochSetup, | ||||||
stateEntry.CurrentEpochCommit, | ||||||
stateEntry.NextEpochSetup, | ||||||
stateEntry.NextEpochCommit, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
expectedIdentities, err := flow.BuildIdentityTable( | ||||||
stateEntry.CurrentEpochSetup.Participants, | ||||||
stateEntry.CurrentEpoch.ActiveIdentities, | ||||||
stateEntry.NextEpochSetup.Participants, | ||||||
stateEntry.NextEpoch.ActiveIdentities, | ||||||
flow.EpochParticipationStatusJoining, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
assert.Equal(t, expectedIdentities, richEntry.CurrentEpochIdentityTable, "should be equal to current epoch setup participants + next epoch setup participants") | ||||||
expectedIdentities, err = flow.BuildIdentityTable( | ||||||
stateEntry.NextEpochSetup.Participants, | ||||||
stateEntry.NextEpoch.ActiveIdentities, | ||||||
stateEntry.CurrentEpochSetup.Participants, | ||||||
stateEntry.CurrentEpoch.ActiveIdentities, | ||||||
flow.EpochParticipationStatusLeaving, | ||||||
) | ||||||
assert.NoError(t, err) | ||||||
assert.Equal(t, expectedIdentities, richEntry.NextEpochIdentityTable, "should be equal to next epoch setup participants + current epoch setup participants") | ||||||
}) | ||||||
} | ||||||
|
||||||
// TestProtocolStateEntry_Copy tests if the copy method returns a deep copy of the entry. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that there are still some open and outdated TODOs here regarding documentation. Furthermore, the methods' goDoc differers quite a bit between the interface I put up PR #5116 targeting your branch, which consolidates the goDoc, address the remaining open TODOs, and removes the outdated TODOs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with not populating the Side comment (outside the scope of this PR):
Though, I think for the scope of this PR, it would be beneficial to populate the I implemented this in my PR #5116. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currenlty, we are using the same cache size for both caches flow-go/storage/badger/protocol_state.go Line 67 in e22ad8b
flow-go/storage/badger/protocol_state.go Line 71 in e22ad8b
I don't think that is a good idea for the following reason:
I would suggest to have a dedicated size parameter for each cache. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,8 +20,9 @@ import ( | |
// operations and to speed up access to frequently used identity tables. | ||
// TODO: update naming to IdentityTable | ||
type ProtocolState struct { | ||
db *badger.DB | ||
cache *Cache[flow.Identifier, *flow.RichProtocolStateEntry] | ||
db *badger.DB | ||
cache *Cache[flow.Identifier, *flow.RichProtocolStateEntry] // protocol_state_id -> protocol state | ||
byBlockIdCache *Cache[flow.Identifier, *flow.RichProtocolStateEntry] // block id -> protocol_state_id | ||
} | ||
|
||
var _ storage.ProtocolState = (*ProtocolState)(nil) | ||
|
@@ -48,13 +49,30 @@ func NewProtocolState(collector module.CacheMetrics, | |
return result, nil | ||
} | ||
} | ||
cache := newCache[flow.Identifier, *flow.RichProtocolStateEntry](collector, metrics.ResourceProtocolState, | ||
withLimit[flow.Identifier, *flow.RichProtocolStateEntry](cacheSize), | ||
withStore(noopStore[flow.Identifier, *flow.RichProtocolStateEntry]), | ||
withRetrieve(retrieve)) | ||
|
||
retrieveByBlockID := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.RichProtocolStateEntry, error) { | ||
return func(tx *badger.Txn) (*flow.RichProtocolStateEntry, error) { | ||
var protocolStateID flow.Identifier | ||
err := operation.LookupProtocolState(blockID, &protocolStateID)(tx) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not lookup identity table ID for block (%x): %w", blockID[:], err) | ||
} | ||
return cache.Get(protocolStateID)(tx) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a minor preference for moving this second cache There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean changing signature of secondary cache to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, exactly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} | ||
byBlockIdCache := newCache[flow.Identifier, *flow.RichProtocolStateEntry](collector, metrics.ResourceProtocolStateByBlockID, | ||
withLimit[flow.Identifier, *flow.RichProtocolStateEntry](cacheSize), | ||
withStore(noopStore[flow.Identifier, *flow.RichProtocolStateEntry]), | ||
withRetrieve(retrieveByBlockID)) | ||
|
||
return &ProtocolState{ | ||
db: db, | ||
cache: newCache[flow.Identifier, *flow.RichProtocolStateEntry](collector, metrics.ResourceProtocolState, | ||
withLimit[flow.Identifier, *flow.RichProtocolStateEntry](cacheSize), | ||
withStore(noopStore[flow.Identifier, *flow.RichProtocolStateEntry]), | ||
withRetrieve(retrieve)), | ||
db: db, | ||
cache: cache, | ||
byBlockIdCache: byBlockIdCache, | ||
} | ||
} | ||
|
||
|
@@ -94,7 +112,7 @@ func (s *ProtocolState) Index(blockID flow.Identifier, protocolStateID flow.Iden | |
func (s *ProtocolState) ByID(id flow.Identifier) (*flow.RichProtocolStateEntry, error) { | ||
tx := s.db.NewTransaction(false) | ||
defer tx.Discard() | ||
return s.byID(id)(tx) | ||
return s.cache.Get(id)(tx) | ||
} | ||
|
||
// ByBlockID retrieves the identity table by the respective block ID. | ||
|
@@ -104,28 +122,7 @@ func (s *ProtocolState) ByID(id flow.Identifier) (*flow.RichProtocolStateEntry, | |
func (s *ProtocolState) ByBlockID(blockID flow.Identifier) (*flow.RichProtocolStateEntry, error) { | ||
tx := s.db.NewTransaction(false) | ||
defer tx.Discard() | ||
return s.byBlockID(blockID)(tx) | ||
} | ||
|
||
// byID retrieves the identity table by its ID. Error returns: | ||
// - storage.ErrNotFound if no identity table with the given ID exists | ||
func (s *ProtocolState) byID(protocolStateID flow.Identifier) func(*badger.Txn) (*flow.RichProtocolStateEntry, error) { | ||
return s.cache.Get(protocolStateID) | ||
} | ||
|
||
// byBlockID retrieves the identity table by the respective block ID. | ||
// TODO: clarify whether the blockID is the block that defines this identity table or the _child_ block where the identity table is applied. CAUTION: surface for bugs! | ||
// Error returns: | ||
// - storage.ErrNotFound if no identity table for the given blockID exists | ||
func (s *ProtocolState) byBlockID(blockID flow.Identifier) func(*badger.Txn) (*flow.RichProtocolStateEntry, error) { | ||
return func(tx *badger.Txn) (*flow.RichProtocolStateEntry, error) { | ||
var protocolStateID flow.Identifier | ||
err := operation.LookupProtocolState(blockID, &protocolStateID)(tx) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not lookup identity table ID for block (%x): %w", blockID[:], err) | ||
} | ||
return s.byID(protocolStateID)(tx) | ||
} | ||
return s.byBlockIdCache.Get(blockID)(tx) | ||
} | ||
|
||
// newRichProtocolStateEntry constructs a rich protocol state entry from a protocol state entry. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the following lines are sanity checks for the previously-constructed
stateEntry
, correct?flow-go/model/flow/protocol_state_test.go
Lines 141 to 143 in e22ad8b
Would suggest to include a comment:
👉 Implemented in my PR #5116
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this is santity checks to ensure we are testing the correct thing