Performance
Device snapshot cached asArc<Device> (#808)
PersistenceManager::get_device_snapshot() previously deep-cloned the entire Device struct on every call — two Jids, 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 longerasync.generate_message_id(): no longerasync.
.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.
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.