fix(export): make export clientId collision-safe to prevent concurrent lost-update#10458
fix(export): make export clientId collision-safe to prevent concurrent lost-update#10458davidfirst wants to merge 4 commits into
Conversation
…t lost-update The clientId is the pending-dir name and the cross-client export lock (export-validate's waitIfNeeded queue sorts pending-dir names; only the first proceeds to validate+persist). A bare Date.now() isn't collision- safe: two exports to the same remote within the same millisecond get the same clientId, share one pending-dir, collapse the queue, and both validate against the pre-persist state, silently losing one update. Append a random suffix so same-ms exports can't collide, keeping the timestamp prefix so the sorted queue still preserves arrival order.
PR Summary by QodoFix export clientId collisions by adding random suffix for concurrent runs Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1. Weak clientId entropy
|
…he new unique format clientId is no longer a pure millisecond timestamp; update the stale PushOptions.clientId doc and the ClientIdInUse error text accordingly.
|
Code review by qodo was updated up to the latest commit 135e445 |
|
Thanks @qodo — both findings were the same point (the now-stale "timestamp in ms" / "exact same millisecond" wording, since
The re-review now reports Bugs (0). (Findings were posted as issue comments, so there's no inline thread to resolve.) |
Thanks — that makes sense.
Looks good to me. |
|
Code review by qodo was updated up to the latest commit 6e7928c |
Problem
export'sclientIdis both the pending-dir name and the cross-client export lock —export-validate'swaitIfNeededqueue sorts pending-dir names and lets only the first proceed to validate+persist. It was generated asDate.now().toString(), which isn't collision-safe.When two exports hit the same remote within the same millisecond (e.g. concurrent CI runners pushing the same lane), they get the same clientId, share one pending-dir, collapse the queue to a single entry, and both validate against the pre-persist state — silently losing one runner's update (no divergence detected, no rebase).
This surfaced as an intermittently-failing e2e test (
ci-commands.e2e.ts→ "concurrent runners snapping the SAME component"): both runners exported cleanly and one snap was dropped from the lane. The outcome depended purely on OS scheduling of the two processes.Fix
Append a random suffix to the generated clientId so same-millisecond exports can't collide. The timestamp prefix is kept so the sorted queue still roughly preserves arrival order.
--resumeis unaffected (it echoes back a user-supplied id; clientId is never parsed numerically).