> ## Documentation Index
> Fetch the complete documentation index at: https://whatsapp-rust.jlucaso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# June 9, 2026 — App state sync: move-not-clone through blocking handoff

> process_patch_list no longer deep-clones the SyncdSnapshot or each SyncdPatch to satisfy the 'static bound on spawn_blocking. The data moves into each closure and comes back via the return tuple, eliminating multi-MB copies on bootstrap and resume.

## Performance

**App state sync: move-not-clone through the blocking thread handoff ([#818](https://github.com/oxidezap/whatsapp-rust/pull/818))**

`process_patch_list` offloads snapshot and patch processing to `runtime::blocking`, whose closure requires `'static`. Previously, the only way to satisfy that bound was to deep-clone the data before moving it in:

| eliminated copy                  | shape                          | measured cost              |
| -------------------------------- | ------------------------------ | -------------------------- |
| `snapshot.clone()` per bootstrap | \~20k records × 1 KB (\~21 MB) | \~8.4 ms, \~60k allocs     |
| `patch.clone()` per resume patch | \~1k mutations × 2 KB (\~2 MB) | \~305 µs, \~3k allocs      |
| `keys_map.clone()` per patch     | key-cache `HashMap`            | now an `Arc` refcount bump |

**How it works.** The snapshot is taken out of `PatchList` with `Option::take`, moved into the blocking closure, returned through the result tuple, and restored to `pl.snapshot` before the function returns. Patches are drained with `mem::take`, processed one by one the same way, and reassembled in order into `pl.patches`. The key cache is wrapped in `Arc` once so each per-patch closure handoff is a cheap refcount increment.

The staleness guard runs on a borrow before the take, so its behavior is unchanged. On any error path `?` propagates before the caller can observe the intermediate empty fields.

The caller contract (`get_missing_key_ids` and has-more bookkeeping read `pl.snapshot` and `pl.patches` after the call) is protected by a regression test.

## Breaking changes

None. `process_patch_list` keeps its existing signature and returns a `PatchList` carrying the same data as before.
