feat(history): let the durability hook capture history-sync chunks before their receipt

Merged
I-
24d ago
+233-5

Problem

process_history_sync_task_tracked sends the hist_sync receipt as soon as it starts on a chunk, before an external blob is even downloaded. Once that receipt is out, the phone treats the chunk as delivered and never uploads it again. So a failed blob download, a failed write in the consumer, or a crash between the receipt and the consumer's commit loses that part of the history for good.

InboundDurabilityHook already closes this gap for live messages: their ack waits until on_messages has committed the batch. History-sync chunks have no equivalent, so a consumer that needs a complete history cannot get it.

Change

  • InboundDurabilityHook::on_history_sync(client, message_id, sync_type, compressed) is a new method with a default implementation, so existing hooks keep compiling. It receives the chunk exactly as the phone uploaded it (zlib-compressed HistorySync), whether it arrived inline or as a downloaded blob.
  • With a hook registered, the hist_sync receipt is sent only after the chunk is downloaded and on_history_sync returned Ok. If the download fails, or the hook returns Err, no receipt is sent, so the phone uploads the chunk again. The contract is at-least-once, like on_messages: captures must be idempotent by message_id.
  • Without a hook nothing changes. Chunks that are not processed (skip_history_sync, or rejected by the admission policy) are still acknowledged right away, without calling the hook.

Tests

  • history_sync_receipt_is_sent_without_a_durability_hook: the receipt is still sent when no hook is registered.
  • history_sync_receipt_waits_until_the_durability_hook_captured_the_chunk: the hook gets the message id, sync type and the exact compressed bytes. No frame reaches the wire before the hook runs, and exactly one receipt follows it.
  • history_sync_receipt_is_withheld_when_the_durability_hook_fails: a failing hook leaves the chunk unacknowledged.
  • Without the change, the last two fail.
  • Commands run: cargo test -p whatsapp-rust --lib (all pass except client_size_pins_runtime_cache_config_saving, which also fails on main on aarch64-apple-darwin: 4496 vs 4376), cargo clippy -p whatsapp-rust --all-targets -- -D warnings, cargo fmt --all --check, cargo test -p whatsapp-rust --doc.

We carry this as a vendored patch in a consumer that stores every chunk before acknowledging it, and have run it against a real account's history sync.