feat(replication): gracefully drain in-flight blob sends before worker shutdown#529
Conversation
|
📖 Docs: HarperFast/documentation#569 documents the new |
There was a problem hiding this comment.
Code Review
This pull request introduces a per-worker tracker (blobSendDrain.ts) to gracefully drain in-flight replication blob sends before a worker shuts down, preventing data divergence on the receiver side. It registers this shutdown drain in replicator.ts and integrates tracking hooks in replicationConnection.ts, accompanied by comprehensive unit tests. The review feedback suggests using optional chaining when accessing ws._socket (e.g., ws._socket?.writableNeedDrain) to prevent potential TypeError crashes if the socket is closed or destroyed mid-stream.
ab08bf1 to
336392c
Compare
|
Reviewed; no blockers found. |
…r shutdown Fix (2) of the deploy-reload blob-divergence root cause (harper-pro#527 is the receiver-side fix (1)). A rolling http_worker restart (deploy reload) was tearing down a worker mid-stream while it was SENDING a replication blob to a peer; the peer then treated the blob as diverged until its idle watchdog fired and stamped a PENDING stub. replication/blobSendDrain.ts tracks in-flight blob sends and their per-chunk progress. sendBlobs registers each send, marks progress as bytes are written (including flushing the terminal frame), and stops starting new sends once the worker is draining. The drain (registered against core's shutdownDrain hook) waits only on sends still making progress (reset-on-bytes; stall = no bytes for ~5s), bounded by a 10-minute ceiling; core extends the shutdown backstops only while a send is actually progressing. Bumps the core submodule to include the shutdownDrain hook (harper core PR). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
336392c to
df7b0dd
Compare
New replication config option (HarperFast/harper#1621, HarperFast/harper-pro#529): bounds how long a worker drains in-flight blob sends before shutting down during a restart, so a rolling restart doesn't interrupt a transfer in progress. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # core # replication/replicationConnection.ts
cb1kenobi flagged that sendBlobs's `once('drain')` waits (mid-loop and the
terminal-frame flush) can hang forever if the peer closes/errors the
connection while parked on backpressure: `drain` never fires, so `finally`
never runs and `endBlobSend`/`outstandingBlobsBeingSent` cleanup is skipped,
leaking the drain token in blobSendDrain's module-global registry.
Add waitForDrainOrSocketEnd, which races the drain wait against the socket's
close/error events and the WebSocket wrapper's close event, cleaning up all
listeners once one fires. Use it at both call sites. Exported so the race is
unit-testable with plain EventEmitters.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cherry-pick: feat(replication): gracefully drain in-flight blob sends before worker shutdown (#529 → v5.1)
New replication config option (HarperFast/harper#1621, HarperFast/harper-pro#529): bounds how long a worker drains in-flight blob sends before shutting down during a restart, so a rolling restart doesn't interrupt a transfer in progress. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Fix (2) of the deploy-reload blob-divergence root cause (harper-pro#527 is the receiver-side fix (1)). A rolling
http_workersrestart (deploy reload) was tearing down a worker mid-stream while it was sending a replication blob to a peer; the peer then treated the blob as diverged until its source-idle watchdog fired and stamped a PENDING stub. This drains in-flight blob sends before the worker shuts down, so a healthy transfer finishes instead of being interrupted.Requires the core drain hook — bumps the
coresubmodule to HarperFast/harper#1621 (companion PR).What changed
replication/blobSendDrain.ts(new): per-worker tracker of in-flight blob sends + their per-chunk progress, anddrainBlobSends(deadline)— resolves when every send has finished, stalled (no bytes for ~5s), or the absolute deadline passes.replication/replicationConnection.ts(sendBlobs): registers each send, marks progress as bytes are written (including around backpressure waits and the terminalfinished:trueframe flush), ends it infinally, and stops starting new sends once the worker is draining.replication/replicator.ts: registers the drain against core'sregisterShutdownDrain, gated onhasProgressingBlobSends()(only a progressing send extends the shutdown backstops).Design
replication.blobSendDrainTimeout;0disables). Only a send that goes silent for the stall window is abandoned.Where to look / open items
maxWorkersDown.Unit tests cover the pure helpers (
isSendProgressing,isDrainComplete,hasProgressingBlobSends) and the drain/quiesce behavior. Cross-model reviewed (Codex + Harper-domain adjudication; Codex's 1 blocker + 5 significant all addressed; the Gemini/agyleg hung out and was skipped).Generated by Claude (Opus 4.8).