Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ pub struct Client {
/// LRU cache for device registry (matches WhatsApp Web's 5000 entry limit).
/// Maps user ID to DeviceListRecord for fast device existence checks.
/// Backed by persistent storage.
pub(crate) device_registry_cache: TypedCache<String, wacore::store::traits::DeviceListRecord>,
pub(crate) device_registry_cache:
TypedCache<String, Arc<wacore::store::traits::DeviceListRecord>>,

/// Router for dispatching stanzas to their appropriate handlers
pub(crate) stanza_router: crate::handlers::router::StanzaRouter,
Expand Down
48 changes: 37 additions & 11 deletions src/client/device_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use anyhow::Result;
use log::{debug, info, warn};
use std::sync::Arc;
use wacore_binary::Jid;

use super::Client;
Expand Down Expand Up @@ -132,7 +133,7 @@ impl Client {
// in the backend), not lookup_keys[0] which is our guessed canonical key.
// This ensures consistency between the in-memory cache and the backend.
self.device_registry_cache
.insert(record.user.clone(), record)
.insert(record.user.clone(), Arc::new(record))
.await;
return has_device;
}
Expand Down Expand Up @@ -164,7 +165,7 @@ impl Client {

// Use canonical_key directly as cache key (no extra clone)
self.device_registry_cache
.insert(canonical_key.clone(), record_for_cache)
.insert(canonical_key.clone(), Arc::new(record_for_cache))
.await;

let backend = self.persistence_manager.backend();
Expand Down Expand Up @@ -222,7 +223,7 @@ impl Client {

let record_for_cache = record.clone();
self.device_registry_cache
.insert(canonical_key.clone(), record_for_cache)
.insert(canonical_key.clone(), Arc::new(record_for_cache))
.await;

if canonical_key != original_user {
Expand Down Expand Up @@ -532,7 +533,8 @@ impl Client {

for key in lookup.all_keys() {
if let Some(record) = self.device_registry_cache.get(key).await {
return Some(record);
// Cold load-modify-persist path: callers mutate the owned record.
return Some((*record).clone());
}
}

Expand All @@ -541,7 +543,7 @@ impl Client {
match backend.get_devices(key).await {
Ok(Some(record)) => {
self.device_registry_cache
.insert(record.user.clone(), record.clone())
.insert(record.user.clone(), Arc::new(record.clone()))
.await;
return Some(record);
}
Expand Down Expand Up @@ -583,7 +585,7 @@ impl Client {
Ok(Some(record)) => {
let devices = Self::reconstruct_device_jids(jid, &record);
self.device_registry_cache
.insert(record.user.clone(), record)
.insert(record.user.clone(), Arc::new(record))
.await;
return Some(devices);
}
Expand Down Expand Up @@ -658,7 +660,7 @@ impl Client {
}

self.device_registry_cache
.insert(lid.to_string(), record)
.insert(lid.to_string(), Arc::new(record))
.await;

// Drop the PN-keyed row in both cache and DB. Invalidate
Expand Down Expand Up @@ -713,10 +715,31 @@ mod tests {
};
client
.device_registry_cache
.insert(user.into(), record)
.insert(user.into(), Arc::new(record))
.await;
}

#[tokio::test]
async fn warm_registry_hit_shares_arc_not_deep_clone() {
let client = create_test_client().await;
setup_device_record(&client, "15551112222", &[1, 2]).await;

let a = client
.device_registry_cache
.get("15551112222")
.await
.expect("warm hit");
let b = client
.device_registry_cache
.get("15551112222")
.await
.expect("warm hit");

// A warm registry hit returns a refcount bump of the same allocation, not a deep copy.
assert!(Arc::ptr_eq(&a, &b));
assert_eq!(a.devices.len(), 2);
}

#[tokio::test]
async fn test_resolve_to_canonical_key_unknown_user() {
let client = create_test_client().await;
Expand Down Expand Up @@ -986,7 +1009,7 @@ mod tests {
};
client
.device_registry_cache
.insert("15551234567".to_string(), record)
.insert("15551234567".to_string(), Arc::new(record))
.await;

// Patch: update device 3 key_index to 5
Expand Down Expand Up @@ -1288,7 +1311,7 @@ mod tests {
};
client
.device_registry_cache
.insert("15551234567".into(), record)
.insert("15551234567".into(), Arc::new(record))
.await;

// Warm the sender key device cache
Expand Down Expand Up @@ -1541,7 +1564,10 @@ mod tests {
backend.update_device_list(legacy.clone()).await.unwrap();
// Warm cache under PN to simulate a reader that populated it before
// the mapping was learned.
client.device_registry_cache.insert(pn.into(), legacy).await;
client
.device_registry_cache
.insert(pn.into(), Arc::new(legacy))
.await;

setup_lid_pn(&client, lid, pn).await;

Expand Down
2 changes: 1 addition & 1 deletion src/handlers/notification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,7 @@ mod tests {
};
client
.device_registry_cache
.insert("5511999999999".into(), record)
.insert("5511999999999".into(), Arc::new(record))
.await;

// Simulate identity change notification: type="encrypt" with <identity/> child
Expand Down
2 changes: 1 addition & 1 deletion src/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2634,7 +2634,7 @@ mod tests {
};
client
.device_registry_cache
.insert((*user).into(), record)
.insert((*user).into(), Arc::new(record))
.await;
}

Expand Down
Loading