fix(history-sync): serialise tcToken persistence + drop external doc reference
Addresses 4 PR #390 review findings (Codex P1 + Copilot ×3): #1 Codex P1 (real, MAJOR) — `storeTcTokensFromHistorySync` fire-and-forget created a write race when multiple history-sync chunks arrived in parallel (common during reconnect/QR scan). The function does a non-atomic read-then-merge-then-write: chunk B could read `existing` before chunk A wrote, then commit later and overwrite chunk A's newer timestamp. Even worse, the merged `__index` write could drop JIDs added by the other chunk, leaving stale tcTokens and persistent 463 send errors. FIX: introduced `scheduleHistoryTcTokenSync()` — a module-scoped single-concurrency promise chain that sequentialises all calls in order. The chain swallows prior errors so a single failure can't stall future runs. Call site stays fire-and-forget so the `messaging-history.set` emit is still instant (which is the whole point of PR #389). #2 Copilot (same root as #1) — already covered by the chain. #3+#4 Copilot — code comments referenced `Downloads/InfiniteAPI-Inbound-Latency-Fix-Documentation.md` which is intentionally out-of-tree (user keeps it locally). Replaced the broken refs with self-contained explanations covering the same rationale — future maintainers no longer need an external file to understand the trade-offs. Skipped: CodeRabbit nitpick to extract a `fireAndForgetMappingStore` helper (stylistic, current explicit form makes the lid/pn argument order obvious to reviewers — keeping it). Tests: 35/35 suites, 824/824 still passing. Customizations untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2345,10 +2345,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
//
|
||||
// HISTORICAL: this restores the intent of d73cd28d39 (2026-02-03) which was
|
||||
// partially reverted by c3fc792351 the same day due to a race-condition concern
|
||||
// with migrateSession (kept sync here). storeLIDPNMappings was over-protected.
|
||||
// with migrateSession (kept sync here). storeLIDPNMappings was over-protected:
|
||||
// it persists a mapping that downstream consumers can re-derive from key.*Alt,
|
||||
// while migrateSession actually moves the Signal session record that decrypt()
|
||||
// will load microseconds later — those two have very different criticality.
|
||||
//
|
||||
// DO NOT make migrateSession async — decrypt() depends on it.
|
||||
// See Downloads/InfiniteAPI-Inbound-Latency-Fix-Documentation.md for full context.
|
||||
// DO NOT make migrateSession async — decrypt() depends on the session being at
|
||||
// the correct identifier (LID vs PN) when it runs. Other code paths (USync
|
||||
// device lookup in messages-send.ts) create LID/PN mappings without migrating
|
||||
// the session, so we cannot skip migration even when the mapping already exists.
|
||||
if (!!alt) {
|
||||
const altServer = jidDecode(alt)?.server
|
||||
const primaryJid = msg.key.participant || msg.key.remoteJid!
|
||||
|
||||
Reference in New Issue
Block a user