tests(store-sync): fix flaky reconciliation test on macOS by anchoring timestamps to a fixed baseTime and aligning sync window via offset

Root cause:
- Separate calls to getNowInNanosecondTime() for test message timestamps and sync window boundaries led to mismatched ranges on slower macOS runs.
- This caused in-window messages to be miscategorized as out-window, producing assertion failures (e.g., remoteNeeds.len==0 while diffInWin==18).

Fix:
- Introduce a single fixed baseTime at test start and construct all message timestamps relative to it.
- Define the intended sync window as [baseTime - 1s, baseTime] and compute the reconciliation offset so effective now equals baseTime (offset = actualNow - baseTime).
- Place in-window messages safely inside the window with 50ms margins, and out-window messages well before the window to avoid overlap.
- Use consistent int64 timestamp arithmetic.

Result:
- The test is deterministic and passes reliably on macOS; eliminates the timing race. Recommend running CI matrix (macOS + Ubuntu) to confirm cross-platform stability.
This commit is contained in:
NagyZoltanPeter 2025-10-08 18:09:05 +02:00
parent 797370ec80
commit f097c1d19d
No known key found for this signature in database
GPG Key ID: 3E1F97CF4A7B6F42

View File

@ -522,31 +522,54 @@ suite "Waku Sync: reconciliation":
randomize()
let nowNs = getNowInNanosecondTime()
let sliceStart = Timestamp(uint64(nowNs) - 700_000_000'u64)
let sliceEnd = nowNs
let stepIn = (sliceEnd.int64 - sliceStart.int64) div diffInWin
# Use a fixed base time to eliminate race conditions between macOS and Ubuntu
let baseTime = getNowInNanosecondTime()
let oldStart = Timestamp(uint64(sliceStart) - outOffsetNs)
# The reconciliation will use: calculateTimeRange(relayJitter, syncRange)
# which does: now = getNow() - jitter; return [now - syncRange, now]
# With 1s syncRange and default 20s jitter, the effective sync window will be:
# [actualTime - jitter - 1s, actualTime - jitter]
# We want our sync window to be [baseTime - 1s, baseTime]
# So we need: actualTime - jitter = baseTime
# Therefore: timeOffset should make actualTime equal to baseTime + jitter
const syncRangeNs = 1_000_000_000'i64 # 1 second in nanoseconds
# Define our intended sync window clearly
let syncWindowEnd = Timestamp(baseTime)
let syncWindowStart = Timestamp(baseTime - syncRangeNs)
# Create in-window messages: spread them safely within the sync window
# Use a buffer to ensure they're clearly inside
let inWinStart = Timestamp(syncWindowStart.int64 + 50_000_000'i64) # 50ms after window starts
let inWinEnd = Timestamp(syncWindowEnd.int64 - 50_000_000'i64) # 50ms before window ends
let stepIn = (inWinEnd.int64 - inWinStart.int64) div diffInWin
# Create out-window messages: place them clearly before the sync window
# Start well before the window starts to avoid any overlap
let outWinStart = Timestamp(syncWindowStart.int64 - int64(outOffsetNs))
let stepOut = Timestamp(stepOutNs)
var inWinHashes, outWinHashes: HashSet[WakuMessageHash]
var ts = sliceStart + (Timestamp(stepIn) * 2)
# Create 18 in-window messages
var ts = inWinStart
for _ in 0 ..< diffInWin:
let msg = fakeWakuMessage(ts = Timestamp ts, contentTopic = DefaultContentTopic)
let msg = fakeWakuMessage(ts = ts, contentTopic = DefaultContentTopic)
let hash = computeMessageHash(DefaultPubsubTopic, msg)
server.messageIngress(hash, DefaultPubsubTopic, msg)
inWinHashes.incl hash
ts += Timestamp(stepIn)
ts = oldStart
# Create 20 out-window messages
ts = outWinStart
for _ in 0 ..< diffOutWin:
let msg = fakeWakuMessage(ts = Timestamp ts, contentTopic = DefaultContentTopic)
let msg = fakeWakuMessage(ts = ts, contentTopic = DefaultContentTopic)
let hash = computeMessageHash(DefaultPubsubTopic, msg)
server.messageIngress(hash, DefaultPubsubTopic, msg)
outWinHashes.incl hash
ts += Timestamp(stepOut)
ts += stepOut
check remoteNeeds.len == 0
@ -564,7 +587,19 @@ suite "Waku Sync: reconciliation":
server.stop()
client.stop()
let res = await client.storeSynchronization(some(serverPeerInfo))
# Calculate offset so the reconciliation window is [baseTime - 1s, baseTime]
# calculateTimeRange(now, jitterParam, syncRange):
# now0 = getNow()
# now = now0 - jitterParam
# return [now - syncRange, now]
# To make 'now' equal baseTime, set jitterParam = now0 - baseTime.
let actualNow = getNowInNanosecondTime()
let timeOffset = timer.nanoseconds(actualNow - baseTime)
let res = await client.storeSynchronization(
peerInfo = some(serverPeerInfo),
offset = timeOffset
)
assert res.isOk(), $res.error
check remoteNeeds.len == diffInWin