-
Notifications
You must be signed in to change notification settings - Fork 4
fix replication requests exceeding maxRoundWindow + splitting resends #431
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
base: main
Are you sure you want to change the base?
Changes from all commits
ba41f4e
01093d0
f45ac2b
06bbf59
3f1ec1f
d608f8b
12f19ea
6a8d1c2
523180a
043ac1e
a1009a2
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 |
|---|---|---|
|
|
@@ -32,4 +32,4 @@ go.work.sum | |
| .idea/ | ||
|
|
||
| # tmp folder | ||
| /tmp | ||
| /tmp | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -677,3 +677,54 @@ func TestReplicationResendsFinalizedBlocksThatFailedVerification(t *testing.T) { | |
| require.Equal(t, uint64(1), storage.NumBlocks()) | ||
| require.Equal(t, block, storedBlock) | ||
| } | ||
|
|
||
| func TestReplicationResendSplitsRequests(t *testing.T) { | ||
|
jadalsmail marked this conversation as resolved.
|
||
| // ensures that when replication requests time out, the missing sequences | ||
| // are re-requested split across the nodes that signed the highest observed quorum, just like the initial requests are. | ||
| nodes := []common.NodeID{{1}, {2}, {3}, {4}} | ||
| startSeq := 2 * uint64(simplex.DefaultMaxRoundWindow) | ||
|
|
||
| net := testutil.NewControlledNetwork(t, nodes) | ||
| storageData := createBlocks(t, nodes, startSeq) | ||
|
|
||
| newNodeConfig := func(from common.NodeID) *testutil.TestNodeConfig { | ||
| comm := testutil.NewTestComm(from, net.BasicInMemoryNetwork, rejectReplicationRequests) | ||
| return &testutil.TestNodeConfig{ | ||
| InitialStorage: storageData, | ||
| Comm: comm, | ||
| ReplicationEnabled: true, | ||
| } | ||
| } | ||
|
|
||
| testutil.NewControlledSimplexNode(t, nodes[0], net, newNodeConfig(nodes[0])) | ||
| testutil.NewControlledSimplexNode(t, nodes[1], net, newNodeConfig(nodes[1])) | ||
| testutil.NewControlledSimplexNode(t, nodes[2], net, newNodeConfig(nodes[2])) | ||
| laggingNode := testutil.NewControlledSimplexNode(t, nodes[3], net, &testutil.TestNodeConfig{ | ||
| ReplicationEnabled: true, | ||
| // twice the responder's window: the timed-out sequences span more | ||
| // than a responder is willing to server in a single request | ||
| MaxRoundWindow: startSeq, | ||
|
Collaborator
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. Ideally we would reproduce the problem without specifying unique configuration such as modifying the max round window.
Collaborator
Author
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, but here's the thing, the bug was reproducible with default config before #432, but that fix removed its trigger (outstanding requests can no longer exceed the responder limit when windows match). Since this PR merges both send paths into one function, the aggregation half can't fire on its own anymore. The window override recreates the same trigger condition #432 eliminated, so the test actually exercises the drop-and-wedge path and proves the new code survives it.
Collaborator
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.
Can you explain what you mean by:
?
Collaborator
Author
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. Drop the requests that are coming from the peer nodes.
Collaborator
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 do not expect to create a scenario that is influenced by existing production code changes from this PR. Is it not possible to create a test with two epochs or more that if I completely revert all production code changes and leave the test intact, the test will fail because of the reason the PR claims to be the issue? |
||
| }) | ||
|
|
||
| net.StartInstances() | ||
| defer net.StopInstances() | ||
| net.TriggerLeaderBlockBuilder(startSeq) | ||
|
|
||
| // the healthy nodes commit, the lagging node's replciation responses | ||
| // were all dropped, so it is stuck at 0. | ||
| for _, n := range net.Instances { | ||
| if n.E.ID.Equals(laggingNode.E.ID) { | ||
| continue | ||
| } | ||
| n.Storage.WaitForBlockCommit(startSeq) | ||
| } | ||
| require.Equal(t, uint64(0), laggingNode.Storage.NumBlocks()) | ||
| // wait to register the timeout before healing | ||
| time.Sleep(100 * time.Millisecond) | ||
| // network healed, responsed are no longer dropped | ||
| net.SetAllNodesMessageFilter(testutil.AllowAllMessages) | ||
| // trigger the resend of all timed-out responses. If the resend path doesn't split/ doesn't account for maxRoundWindow | ||
| // this test would hang forever because the responses would be dropped silently. | ||
| laggingNode.E.AdvanceTime(laggingNode.E.StartTime.Add(simplex.DefaultReplicationRequestTimeout * 2)) | ||
| laggingNode.Storage.WaitForBlockCommit(startSeq) | ||
| } | ||
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.
lets undo this diff