perf(device-registry): cache Arc<DeviceListRecord> to avoid deep clone on warm hits - #703
Conversation
…e on warm hits get_devices_from_registry and has_device read device_registry_cache on the warm device-resolution path (per recipient on DM send, per participant on group fanout). moka's get clones the value, so each hit deep-copied the whole DeviceListRecord only for the callers to borrow it read-only. Store Arc<DeviceListRecord>: a warm hit is now a refcount bump. The borrow-only callers are unchanged; load_device_record (cold load-modify-persist) clones the inner value to keep returning an owned record.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThe device registry cache storage type is upgraded from owning ChangesDevice Registry Cache Arc Wrapping
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested reviewers
Look, this is straightforward refactoring, but it needs to be airtight. The Arc wrapping pattern is applied uniformly across all cache insertion paths—cold-load paths, record updates, migrations, and test setup. Every single cache write goes through Arc now. The warm-hit read path gets the deref-and-clone semantics right so callers still get owned values without breaking expectations. Tests validate the behavioral change explicitly: the new test verifies Arc pointer equality on warm hits, meaning allocations are actually shared, not deep-cloned. That's the whole point here—we're squeezing efficiency out of the cache layer by not duplicating records on every warm access. The changes span multiple files—device registry, handlers, send module—but they're all mechanical: find the cache insert, wrap it in Arc::new(). Find the test setup, do the same. This type of consistency work either gets done right everywhere or it doesn't work at all. Check that every insertion point is covered and none were missed. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Benchmark Results67 unchanged benchmark(s)
|
Problem
get_devices_from_registryandhas_devicereaddevice_registry_cache(a moka-backedTypedCache) on the warm device-resolution path: once per recipient on every DM send, and once per participant on group fanout. moka'sgetreturns the value by clone, so each cache hit deep-copies the wholeDeviceListRecord(aStringuser +Vec<DeviceInfo>+Option<String>phash + ...) only for the callers to borrow it read-only (reconstruct_device_jids(jid, &record),record.devices.iter().any(...)). The deep clone is pure throwaway, on the cache-HIT path that runs per recipient / participant.Change
Store the cache value as
Arc<DeviceListRecord>. A warm hit now returns anArcclone (a refcount bump) instead of deep-copying the record; the two hot borrow-only callers work unchanged via deref, and inserts wrap the record inArc::new.serde'srcfeature (already enabled) letsArc<DeviceListRecord>satisfy the cache'sSerialize/DeserializeOwnedbounds used by the optional custom-store backend.The one cold caller that needs an owned, mutable record (
load_device_record, the load-modify-persist path) clones the inner value on a cache hit ((*arc).clone()). That re-introduces a clone, but only off the per-send hot path, so it stays a clear net win.Tests
New
warm_registry_hit_shares_arc_not_deep_clone: two warm gets of the same key returnArcs pointing to the same allocation (Arc::ptr_eq), proving the hit is a refcount bump rather than a deep copy.Breaking
None.
device_registry_cacheispub(crate); the public API and all observable behavior are unchanged.