diff --git a/advanced/signal-protocol.mdx b/advanced/signal-protocol.mdx index cdfa96d..c9f0984 100644 --- a/advanced/signal-protocol.mdx +++ b/advanced/signal-protocol.mdx @@ -861,10 +861,10 @@ When sending group, status, or DM messages, the library validates the participan **How it works:** 1. Before sending, the client obtains the locally computed `phash` — from the stanza `phash` attribute for group/status messages, or from `PreparedDmStanza.phash` for DMs -2. A oneshot ack waiter is registered for the message ID via `register_ack_waiter` +2. A `PhashWaiter` (expected hash, target JID, whether to also invalidate the group cache) is registered for the message ID via `register_phash_waiter` — a map entry, not a channel or a task 3. The message stanza is sent to the server -4. A background task (`spawn_phash_validation`) awaits the server's ack (with a 10-second timeout) -5. The server's ack includes its own `phash` — if it differs from the local value, the client invalidates caches +4. When the server's ack for that message ID arrives, the read loop compares its `phash` attribute against the expected value inline, with no task involved +5. On a match the entry is just dropped; on a mismatch the client spawns a task to invalidate caches — so a send only pays for a task in the uncommon case, not on every send ([#1116](https://github.com/oxidezap/whatsapp-rust/pull/1116)) **On mismatch, the following caches are invalidated:** @@ -895,7 +895,7 @@ if !jid.is_group() && !jid.is_status_broadcast() { ``` -The phash validation runs asynchronously in the background and does not block the send path. If the server ack times out (after 10 seconds) or the oneshot channel is dropped, the validation is silently skipped. This matches WhatsApp Web's approach of using phash as a best-effort staleness detector rather than a hard requirement. +The phash check never blocks the send path. If the server's ack never arrives, nothing polls the waiter directly — it is swept out on the keepalive tick, and is guaranteed to survive the sweep immediately following its registration (so a waiter is never dropped mid-flight) but is removed on the sweep after that. Since each keepalive tick lands 15–30 seconds after the last, the actual time-to-live is roughly one to two tick intervals — about 15 to 60 seconds, depending on where registration falls relative to the sweep cycle — rather than the old fixed 10-second timeout. The sweep runs before keepalive's own idle early-return, so a connection with steady inbound traffic — which skips sending pings — still gets its stale waiters cleared; a stranded waiter would otherwise read as an outstanding IQ and suppress pings for the life of the connection. This matches WhatsApp Web's approach of using phash as a best-effort staleness detector rather than a hard requirement. #### WA Web phash parity (v0.6) diff --git a/api/client.mdx b/api/client.mdx index 9d9a6b0..b119ca3 100644 --- a/api/client.mdx +++ b/api/client.mdx @@ -2029,6 +2029,8 @@ Entry counts plus estimated retained heap bytes for the client's internal collec | `group_distribution_lock_evictions` | `u64` | Cumulative capacity evictions of cold (non-live) distribution lanes; poll successive reports to derive a rate | | `group_distribution_lock_eviction_blocks` | `u64` | Cumulative attempts that kept a live lane and temporarily exceeded `group_distribution_locks_capacity` instead of evicting it | | `resend_rate_limiter_chats` | `u64` | Chats tracked by the per-chat resend rate limiter (count only) | +| `transport_ack_queue` | `usize` | Deferred transport acks queued for the ack worker. Each entry retains the full inbound node plus a flush guard, so a stalled transport shows up here as a growing backlog ([#1116](https://github.com/oxidezap/whatsapp-rust/pull/1116)) | +| `delivery_receipt_queue` | `usize` | Delivery receipts queued for their worker, same shape as `transport_ack_queue` ([#1116](https://github.com/oxidezap/whatsapp-rust/pull/1116)) | | `response_waiters` | `usize` | Active IQ response waiters | | `node_waiters` | `usize` | Active node waiters | | `pending_retries` | `usize` | Pending message retries |