-
Notifications
You must be signed in to change notification settings - Fork 214
[Testing] Fix data race in cluster-prefix RecordCache #8627
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
Changes from 1 commit
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 |
|---|---|---|
|
|
@@ -1461,6 +1461,55 @@ func TestNewControlMsgValidationInspector_validateClusterPrefixedTopic(t *testin | |
| cancel() | ||
| unittest.RequireCloseBefore(t, inspector.Done(), 5*time.Second, "inspector did not stop") | ||
| }) | ||
|
|
||
| t.Run("validateClusterPrefixedTopic with multiple workers disseminates between 1 and one-per-message notifications past the hard threshold", func(t *testing.T) { | ||
| // total number of inspected messages; with a hard threshold of 10, the last 5 exceed it | ||
| const total = 15 | ||
| logCounter := atomic.NewInt64(0) | ||
| logger := hookedLogger(logCounter, zerolog.TraceLevel, worker.QueuedItemProcessedLog) | ||
| notificationCount := atomic.NewInt64(0) | ||
| inspector, signalerCtx, cancel, consumer, rpcTracker, sporkID, idProvider, topicProviderOracle := inspectorFixture(t, func(params *validation.InspectorParams) { | ||
| params.Config.ClusterPrefixedMessage.HardThreshold = 10 | ||
| params.Config.GraftPrune.InvalidTopicIdThreshold = 0 | ||
| // This test deliberately keeps the default (multi-)worker count and documents the | ||
| // concurrency-tolerant contract: the cluster-prefix tracker increment and the subsequent | ||
| // hard-threshold check are not atomic, so each of the `total` concurrent inspections may | ||
| // observe the tracker above the hard threshold and disseminate a notification. At least | ||
| // one notification is guaranteed (the globally last inspection observes all increments), | ||
| // and at most one notification per inspected message can be disseminated. | ||
|
Comment on lines
+1472
to
+1479
Contributor
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. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Assert the exact threshold-exceeded notification count. The locked gauge capture gives each inspection its own post-increment gauge. With a threshold of 10 and 15 inspections, only inspections 11 through 15 can notify. The current upper bound of Proposed fix- const total = 15
+ const (
+ total = 15
+ hardThreshold = 10
+ )
...
- params.Config.ClusterPrefixedMessage.HardThreshold = 10
+ params.Config.ClusterPrefixedMessage.HardThreshold = hardThreshold
...
- count := notificationCount.Load()
- require.GreaterOrEqual(t, count, int64(1), "at least one inspection past the hard threshold must disseminate a notification")
- require.LessOrEqual(t, count, int64(total), "at most one notification per inspected message may be disseminated")
+ require.Equal(t, int64(total-hardThreshold), notificationCount.Load())Also applies to: 1507-1509 🤖 Prompt for AI Agents |
||
| params.Logger = logger | ||
| }) | ||
| clusterID := flow.ChainID(unittest.IdentifierFixture().String()) | ||
| clusterPrefixedTopic := channels.Topic(fmt.Sprintf("%s/%s", channels.SyncCluster(clusterID), sporkID)).String() | ||
| topicProviderOracle.UpdateTopics([]string{clusterPrefixedTopic}) | ||
| from := unittest.PeerIdFixture(t) | ||
| identity := unittest.IdentityFixture() | ||
| idProvider.On("ByPeerID", from).Return(identity, true).Times(total) | ||
| checkNotification := checkNotificationFunc(t, from, p2pmsg.CtrlMsgGraft, validation.IsInvalidTopicIDThresholdExceeded, p2p.CtrlMsgTopicTypeClusterPrefixed) | ||
| inspectMsgRpc := unittest.P2PRPCFixture(unittest.WithGrafts(unittest.P2PRPCGraftFixture(&clusterPrefixedTopic))) | ||
| inspector.ActiveClustersChanged(flow.ChainIDList{flow.ChainID(unittest.IdentifierFixture().String())}) | ||
| rpcTracker.On("LastHighestIHaveRPCSize").Return(int64(100)).Maybe() | ||
| consumer.On("OnInvalidControlMessageNotification", mock.AnythingOfType("*p2p.InvCtrlMsgNotif")).Return(nil).Run(func(args mock.Arguments) { | ||
| notificationCount.Inc() | ||
| checkNotification(args) | ||
| }) | ||
| inspector.Start(signalerCtx) | ||
| unittest.RequireComponentsReadyBefore(t, 1*time.Second, inspector) | ||
|
|
||
| for i := 0; i < total; i++ { | ||
| require.NoError(t, inspector.Inspect(from, inspectMsgRpc)) | ||
| } | ||
| // wait until all inspections have been processed by the workers | ||
| require.Eventually(t, func() bool { | ||
| return logCounter.Load() == total | ||
| }, time.Second, 100*time.Millisecond) | ||
|
|
||
| count := notificationCount.Load() | ||
| require.GreaterOrEqual(t, count, int64(1), "at least one inspection past the hard threshold must disseminate a notification") | ||
| require.LessOrEqual(t, count, int64(total), "at most one notification per inspected message may be disseminated") | ||
| cancel() | ||
| unittest.RequireCloseBefore(t, inspector.Done(), 5*time.Second, "inspector did not stop") | ||
| }) | ||
| } | ||
|
|
||
| // TestControlMessageValidationInspector_ActiveClustersChanged validates the expected update of the active cluster IDs list. | ||
|
|
||
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: onflow/flow-go
Length of output: 9038
🏁 Script executed:
Repository: onflow/flow-go
Length of output: 5704
Require processing of the proposer vote.
prepareMockedProcessorsetsProcess(proposerVote)to.Maybe(), whileexpectedProposerVoteProcessingsonly controlsOnVoteProcessed. This makes the cached proposer-vote replay assertions in tests likeTestProcessBlock_ProcessingOfCachedVotesandTestProcessBlock_ByzantineLeaderSpamming_ProposalAfterVotepass without a requiredProcess(proposerVote)expectation. Make the helper also set.Times(expectedProposerVoteProcessings)forProcess(proposerVote)when the count is greater than zero.🤖 Prompt for AI Agents