Skip to content

Commit 650e49a

Browse files
authored
fix(sync): skip AOF backlog when sync_aof=false
* test(sync): reproduce sync_aof false buffering Assisted-by: Codex:gpt-5.5 * fix(sync): skip AOF receiver when disabled Assisted-by: Codex:gpt-5.5 * chore(ci): remove focused sync_aof workflow Assisted-by: Codex:gpt-5.5
1 parent 46a3388 commit 650e49a

3 files changed

Lines changed: 111 additions & 4 deletions

File tree

internal/reader/sync_standalone_reader.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,12 @@ func (r *syncStandaloneReader) StartReadWithPSync(ctx context.Context) []chan *e
159159
r.sendPSync()
160160
rdbFilePath := r.receiveRDB()
161161
startOffset := r.stat.AofReceivedOffset
162-
go r.sendReplconfAck() // start sent replconf ack
163-
go r.receiveAOF()
162+
if r.opts.SyncAof {
163+
go r.sendReplconfAck() // start sent replconf ack
164+
go r.receiveAOF()
165+
} else {
166+
r.client.Close()
167+
}
164168
if r.opts.SyncRdb {
165169
r.sendRDB(rdbFilePath)
166170
}
@@ -182,7 +186,11 @@ func (r *syncStandaloneReader) StartReadWithSync(ctx context.Context) []chan *en
182186
r.sendSync()
183187
rdbFilePath := r.receiveRDB()
184188
startOffset := r.stat.AofReceivedOffset
185-
go r.receiveAOF()
189+
if r.opts.SyncAof {
190+
go r.receiveAOF()
191+
} else {
192+
r.client.Close()
193+
}
186194
if r.opts.SyncRdb {
187195
r.sendRDB(rdbFilePath)
188196
}

tests/cases/sync_aof_false.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import os
2+
import time
3+
4+
import pybbt
5+
6+
import helpers as h
7+
from helpers.utils.timer import Timer
8+
9+
10+
def _reader_aof_files(reader_dir):
11+
if not reader_dir or not os.path.isdir(reader_dir):
12+
return []
13+
return sorted(name for name in os.listdir(reader_dir) if name.endswith(".aof"))
14+
15+
16+
def _wait_for_process_exit(shake, timeout):
17+
timer = Timer()
18+
while shake.server.is_running():
19+
if timer.elapsed() > timeout:
20+
with open(f"{shake.dir}/data/shake.log") as f:
21+
pybbt.log(f.read())
22+
raise TimeoutError(f"redis-shake did not exit within {timeout}s")
23+
time.sleep(0.1)
24+
25+
26+
def _wait_for_reader_status(shake, expected_status, timeout):
27+
timer = Timer()
28+
last_status = None
29+
while True:
30+
status = shake.get_status()
31+
reader = status["reader"]
32+
if reader is not None:
33+
last_status = reader["status"]
34+
if last_status in expected_status:
35+
return reader
36+
37+
if timer.elapsed() > timeout:
38+
with open(f"{shake.dir}/data/shake.log") as f:
39+
pybbt.log(f.read())
40+
raise TimeoutError(
41+
f"reader did not reach {expected_status} within {timeout}s, "
42+
f"last status={last_status}"
43+
)
44+
time.sleep(0.1)
45+
46+
47+
@pybbt.case()
48+
def sync_aof_false_does_not_buffer_incremental_stream():
49+
src = h.Redis()
50+
dst = h.Redis()
51+
52+
pipe = src.pipeline()
53+
for i in range(500):
54+
pipe.set(f"sync_aof_false:rdb:{i}", "x" * 128)
55+
pipe.execute()
56+
src.do("save")
57+
58+
opts = h.ShakeOpts.create_sync_opts(src, dst)
59+
opts["sync_reader"]["sync_aof"] = False
60+
opts["advanced"] = {
61+
"pipeline_count_limit": 1,
62+
"target_redis_max_qps": 20,
63+
}
64+
shake = h.Shake(opts)
65+
66+
reader = _wait_for_reader_status(shake, {"syncing rdb"}, timeout=20)
67+
68+
aof_keys = {f"sync_aof_false:aof:{i}": f"value-{i}" for i in range(20)}
69+
for key, value in aof_keys.items():
70+
src.do("set", key, value)
71+
72+
observed_reader_dir = reader["dir"]
73+
timer = Timer()
74+
while timer.elapsed() < 5:
75+
status = shake.get_status()
76+
reader = status["reader"]
77+
if reader is None:
78+
time.sleep(0.1)
79+
continue
80+
observed_reader_dir = reader["dir"]
81+
aof_bytes = reader["aof_received_bytes"]
82+
aof_files = _reader_aof_files(observed_reader_dir)
83+
if aof_bytes != 0 or aof_files:
84+
raise AssertionError(
85+
f"sync_aof=false buffered AOF data: bytes={aof_bytes}, files={aof_files}"
86+
)
87+
time.sleep(0.1)
88+
89+
pybbt.ASSERT_TRUE(observed_reader_dir is not None)
90+
pybbt.ASSERT_EQ(_reader_aof_files(observed_reader_dir), [])
91+
92+
_wait_for_process_exit(shake, timeout=30)
93+
94+
for i in range(0, 500, 100):
95+
pybbt.ASSERT_EQ(dst.do("get", f"sync_aof_false:rdb:{i}"), b"x" * 128)
96+
for key in aof_keys:
97+
pybbt.ASSERT_EQ(dst.do("get", key), None)

tests/helpers/shake.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ def __init__(self, opts: typing.Dict):
9494
self.src = opts.pop("__src", None)
9595
self.dst = opts.pop("__dst", None)
9696

97-
opts["advanced"] = {"status_port": self.status_port, "log_level": "debug"}
97+
advanced = opts.setdefault("advanced", {})
98+
advanced["status_port"] = self.status_port
99+
advanced.setdefault("log_level", "debug")
98100

99101
self.dir = f"{self.case_ctx.dir}/shake{self.status_port}"
100102
create_empty_dir(self.dir)

0 commit comments

Comments
 (0)