> ## 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 — Device snapshot cached as Arc<Device>

> get_device_snapshot() is now sync and returns Arc<Device>. The related client accessors (get_pn, get_lid, get_push_name, generate_message_id) are also sync. Breaking change: drop .await at all call sites.

## Performance

**Device snapshot cached as `Arc<Device>` ([#808](https://github.com/oxidezap/whatsapp-rust/pull/808))**

`PersistenceManager::get_device_snapshot()` previously deep-cloned the entire `Device` struct on every call — two `Jid`s, `push_name`, `props_hash`, `edge_routing_info`, `nct_salt`, roughly 5–8 heap allocations. With \~72 call sites including once per inbound message, this added up.

`PersistenceManager` now keeps a `device_snapshot: std::sync::RwLock<Arc<Device>>` that is rebuilt **under the existing device write guard** inside `modify_device` — the single mutation funnel. Mutations are rare (pairing, push-name sync, prekey counter); reads now pay only a `std::sync::RwLock` read plus a refcount bump, with no clone and no contention against writers.

**Breaking changes:**

* **`get_device_snapshot()`**: `async fn → Device` → `fn → Arc<Device>`.
* **`get_pn()` / `get_lid()` / `get_push_name()` / `require_pn()`**: no longer `async`.
* **`generate_message_id()`**: no longer `async`.

**Migration is mechanical**: drop `.await` at every call site. The return of `get_device_snapshot()` is now `Arc<Device>` — borrow fields directly from the held `Arc` (`snapshot.pn.as_ref()`), or clone individual fields where ownership is needed. Do not hold the snapshot longer than the current scope; doing so pins an old `Arc` allocation rather than paying for a fresh borrow.

```rust theme={null}
// Before
let device = client.persistence_manager().get_device_snapshot().await;
let pn = device.pn.clone();

let push_name = client.get_push_name().await;
let id = client.generate_message_id().await;

// After
let snapshot = client.persistence_manager().get_device_snapshot();   // fn, no .await
let pn = snapshot.pn.clone();

let push_name = client.get_push_name();         // fn, no .await
let id = client.generate_message_id();          // fn, no .await
```

`get_device_arc()` is kept but its role is narrowed to store-adapter internals that need `&mut Device` trait access. All plain read paths should use `persistence_manager().get_device_snapshot()` instead.

**No other breaking changes.** `modify_device`, `process_command`, and `DeviceCommand` are unchanged.
