Skip to content

Commit 476b4f0

Browse files
committed
rafttest: test lazy replication
Epic: none Release note: none
1 parent 19a1b81 commit 476b4f0

5 files changed

+121
-31
lines changed

pkg/raft/rafttest/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ go_library(
2020
"interaction_env_handler_raft_log.go",
2121
"interaction_env_handler_raftstate.go",
2222
"interaction_env_handler_report_unreachable.go",
23+
"interaction_env_handler_send_msgapp.go",
2324
"interaction_env_handler_send_snapshot.go",
2425
"interaction_env_handler_set_randomized_election_timeout.go",
2526
"interaction_env_handler_stabilize.go",

pkg/raft/rafttest/interaction_env_handler.go

+6
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ func (env *InteractionEnv) Handle(t *testing.T, d datadriven.TestData) string {
198198
// propose-conf-change 2 v1=true
199199
// v5
200200
err = env.handleProposeConfChange(t, d)
201+
202+
case "send-msg-app":
203+
// Send a MsgApp from the leader to a peer.
204+
// Example: send-msg-app 1 to=2 lo=10 hi=20
205+
err = env.handleSendMsgApp(t, d)
206+
201207
case "report-unreachable":
202208
// Calls <1st>.ReportUnreachable(<2nd>).
203209
//

pkg/raft/rafttest/interaction_env_handler_add_nodes.go

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ func (env *InteractionEnv) handleAddNodes(t *testing.T, d datadriven.TestData) e
5757
arg.Scan(t, i, &snap.Data)
5858
case "async-storage-writes":
5959
arg.Scan(t, i, &cfg.AsyncStorageWrites)
60+
case "lazy-replication":
61+
arg.Scan(t, i, &cfg.LazyReplication)
6062
case "prevote":
6163
arg.Scan(t, i, &cfg.PreVote)
6264
case "checkquorum":
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2024 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package rafttest
12+
13+
import (
14+
"fmt"
15+
"math"
16+
"testing"
17+
18+
"github.com/cockroachdb/cockroach/pkg/raft"
19+
pb "github.com/cockroachdb/cockroach/pkg/raft/raftpb"
20+
"github.com/cockroachdb/datadriven"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func (env *InteractionEnv) handleSendMsgApp(t *testing.T, d datadriven.TestData) error {
25+
require.Len(t, d.CmdArgs, 4)
26+
from := firstAsNodeIdx(t, d)
27+
var to int
28+
var lo, hi uint64
29+
d.ScanArgs(t, "to", &to)
30+
d.ScanArgs(t, "lo", &lo)
31+
d.ScanArgs(t, "hi", &hi)
32+
return env.SendMsgApp(from, pb.PeerID(to), lo, hi)
33+
}
34+
35+
func (env *InteractionEnv) SendMsgApp(from int, to pb.PeerID, lo, hi uint64) error {
36+
rn := env.Nodes[from].RawNode
37+
snap := rn.LogSnapshot()
38+
ls, err := snap.LogSlice(lo, hi, math.MaxUint64)
39+
if err != nil {
40+
return err
41+
}
42+
if msg, ok := rn.SendMsgApp(to, ls); ok {
43+
env.Output.WriteString(raft.DescribeMessage(msg, defaultEntryFormatter))
44+
env.Messages = append(env.Messages, msg)
45+
} else {
46+
env.Output.WriteString(fmt.Sprintf("could not send MsgApp (%d,%d] to %d", lo, hi, to))
47+
}
48+
return nil
49+
}

pkg/raft/testdata/lazy_replication.txt

+63-31
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
# This is a prep test for the next commit, to demonstrate the difference between
2-
# the eager and lazy replication.
1+
# This test demonstrates the "lazy replication" feature. The leader sends MsgApp
2+
# messages to StateReplication peers only when requested explicitly by the
3+
# application.
34

45
# Skip logging the boilerplate. Set up a raft group of 3 nodes, and elect node 1
56
# as the leader. Nodes 2 and 3 are the followers.
67
log-level none
78
----
89
ok
910

10-
add-nodes 3 voters=(1,2,3) index=10
11+
add-nodes 3 voters=(1,2,3) index=10 lazy-replication=true
1112
----
1213
ok
1314

@@ -32,87 +33,118 @@ propose 1 data-2
3233
----
3334
ok
3435

35-
# Both entries are sent to the followers eagerly, in two separate MsgApp
36-
# messages. The entries are committed, and the followers' logs converge to the
37-
# leader's.
36+
# NB: no entries are sent to the followers yet.
3837
stabilize
3938
----
4039
> 1 handling Ready
4140
Ready MustSync=true:
4241
Entries:
4342
1/12 EntryNormal "data-1"
4443
1/13 EntryNormal "data-2"
45-
Messages:
46-
1->2 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data-1"]
47-
1->3 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data-1"]
48-
1->2 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data-2"]
49-
1->3 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data-2"]
44+
45+
# Attempt to send a misaligned MsgApp. No-op.
46+
send-msg-app 1 to=2 lo=10 hi=13
47+
----
48+
could not send MsgApp (10,13] to 2
49+
50+
# Send a MsgApp to node 2, containing both entries.
51+
send-msg-app 1 to=2 lo=11 hi=13
52+
----
53+
1->2 MsgApp Term:1 Log:1/11 Commit:11 Entries:[
54+
1/12 EntryNormal "data-1"
55+
1/13 EntryNormal "data-2"
56+
]
57+
58+
# Send a MsgApp to node 3, containing only one entry.
59+
send-msg-app 1 to=3 lo=11 hi=12
60+
----
61+
1->3 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data-1"]
62+
63+
# The followers receive the entries and reply to the leader. The leader commits
64+
# both entries, but the replication flow to node 3 still has one pending entry.
65+
stabilize
66+
----
5067
> 2 receiving messages
51-
1->2 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data-1"]
52-
1->2 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data-2"]
68+
1->2 MsgApp Term:1 Log:1/11 Commit:11 Entries:[
69+
1/12 EntryNormal "data-1"
70+
1/13 EntryNormal "data-2"
71+
]
5372
> 3 receiving messages
5473
1->3 MsgApp Term:1 Log:1/11 Commit:11 Entries:[1/12 EntryNormal "data-1"]
55-
1->3 MsgApp Term:1 Log:1/12 Commit:11 Entries:[1/13 EntryNormal "data-2"]
5674
> 2 handling Ready
5775
Ready MustSync=true:
5876
Entries:
5977
1/12 EntryNormal "data-1"
6078
1/13 EntryNormal "data-2"
6179
Messages:
62-
2->1 MsgAppResp Term:1 Log:0/12 Commit:11
6380
2->1 MsgAppResp Term:1 Log:0/13 Commit:11
6481
> 3 handling Ready
6582
Ready MustSync=true:
6683
Entries:
6784
1/12 EntryNormal "data-1"
68-
1/13 EntryNormal "data-2"
6985
Messages:
7086
3->1 MsgAppResp Term:1 Log:0/12 Commit:11
71-
3->1 MsgAppResp Term:1 Log:0/13 Commit:11
7287
> 1 receiving messages
73-
2->1 MsgAppResp Term:1 Log:0/12 Commit:11
7488
2->1 MsgAppResp Term:1 Log:0/13 Commit:11
7589
3->1 MsgAppResp Term:1 Log:0/12 Commit:11
76-
3->1 MsgAppResp Term:1 Log:0/13 Commit:11
7790
> 1 handling Ready
7891
Ready MustSync=true:
7992
HardState Term:1 Vote:1 Commit:13 Lead:1 LeadEpoch:1
8093
CommittedEntries:
8194
1/12 EntryNormal "data-1"
8295
1/13 EntryNormal "data-2"
8396
Messages:
84-
1->2 MsgApp Term:1 Log:1/13 Commit:12
85-
1->3 MsgApp Term:1 Log:1/13 Commit:12
8697
1->2 MsgApp Term:1 Log:1/13 Commit:13
87-
1->3 MsgApp Term:1 Log:1/13 Commit:13
98+
1->3 MsgApp Term:1 Log:1/12 Commit:13
8899
> 2 receiving messages
89-
1->2 MsgApp Term:1 Log:1/13 Commit:12
90100
1->2 MsgApp Term:1 Log:1/13 Commit:13
91101
> 3 receiving messages
92-
1->3 MsgApp Term:1 Log:1/13 Commit:12
93-
1->3 MsgApp Term:1 Log:1/13 Commit:13
102+
1->3 MsgApp Term:1 Log:1/12 Commit:13
94103
> 2 handling Ready
95104
Ready MustSync=true:
96105
HardState Term:1 Vote:1 Commit:13 Lead:1 LeadEpoch:1
97106
CommittedEntries:
98107
1/12 EntryNormal "data-1"
99108
1/13 EntryNormal "data-2"
100109
Messages:
101-
2->1 MsgAppResp Term:1 Log:0/13 Commit:12
102110
2->1 MsgAppResp Term:1 Log:0/13 Commit:13
103111
> 3 handling Ready
104112
Ready MustSync=true:
105-
HardState Term:1 Vote:1 Commit:13 Lead:1 LeadEpoch:1
113+
HardState Term:1 Vote:1 Commit:12 Lead:1 LeadEpoch:1
106114
CommittedEntries:
107115
1/12 EntryNormal "data-1"
116+
Messages:
117+
3->1 MsgAppResp Term:1 Log:0/12 Commit:12
118+
> 1 receiving messages
119+
2->1 MsgAppResp Term:1 Log:0/13 Commit:13
120+
3->1 MsgAppResp Term:1 Log:0/12 Commit:12
121+
122+
# One entry still to be replicated to node 3.
123+
status 1
124+
----
125+
1: StateReplicate match=13 next=14 sentCommit=11 matchCommit=11
126+
2: StateReplicate match=13 next=14 sentCommit=13 matchCommit=13
127+
3: StateReplicate match=12 next=13 sentCommit=13 matchCommit=12
128+
129+
# Replicate it.
130+
send-msg-app 1 to=3 lo=12 hi=13
131+
----
132+
1->3 MsgApp Term:1 Log:1/12 Commit:13 Entries:[1/13 EntryNormal "data-2"]
133+
134+
stabilize
135+
----
136+
> 3 receiving messages
137+
1->3 MsgApp Term:1 Log:1/12 Commit:13 Entries:[1/13 EntryNormal "data-2"]
138+
> 3 handling Ready
139+
Ready MustSync=true:
140+
HardState Term:1 Vote:1 Commit:13 Lead:1 LeadEpoch:1
141+
Entries:
142+
1/13 EntryNormal "data-2"
143+
CommittedEntries:
108144
1/13 EntryNormal "data-2"
109145
Messages:
110-
3->1 MsgAppResp Term:1 Log:0/13 Commit:12
111146
3->1 MsgAppResp Term:1 Log:0/13 Commit:13
112147
> 1 receiving messages
113-
2->1 MsgAppResp Term:1 Log:0/13 Commit:12
114-
2->1 MsgAppResp Term:1 Log:0/13 Commit:13
115-
3->1 MsgAppResp Term:1 Log:0/13 Commit:12
116148
3->1 MsgAppResp Term:1 Log:0/13 Commit:13
117149

118150
# The leader has converged to a fully replicated state.

0 commit comments

Comments
 (0)