Amarula.Protocol.Signal.DeviceListCache (amarula v0.1.0)

View Source

Per-user cache of a contact's device list, ported from Baileys' userDevicesCache (the getUSyncDevices cache). Lets a send skip the USync round-trip when we already know a user's devices.

Keyed by JID user (the part before @), value = the list of device maps %{user, device, server, jid} that USync.Devices.extract/4 produces.

Persistence goes through the pluggable Amarula.Storage seam (:device_list namespace, keyed by user), scoped to the connection conn (Amarula.Conn). Entries carry a stored-at timestamp and expire after @ttl_ms so a stale device list is eventually re-fetched even without a server staleness signal (Baileys uses an LRU with a TTL; phash-based invalidation is disabled upstream). TTL is enforced here in the cache, not in the storage adapter.

Summary

Functions

Drop a user's cached device list (e.g. on a device-list-change notification).

Fetch the cached device list for a JID (or user string). Returns nil on a miss or when the entry has expired.

Look up several users at once. Returns {hits, misses} where hits is a user => devices map and misses is the list of users with no fresh entry.

Store devices for user (overwriting any prior entry). devices is the full list for that user; pass the user JID or bare user string.

Store device lists for many users at once. by_user is user => devices. Returns :ok.

Types

device()

@type device() :: %{
  user: String.t(),
  device: non_neg_integer(),
  server: String.t(),
  jid: String.t()
}

Functions

delete(conn, jid)

@spec delete(Amarula.Conn.t(), String.t()) :: :ok

Drop a user's cached device list (e.g. on a device-list-change notification).

get(conn, jid)

@spec get(Amarula.Conn.t(), String.t()) :: [device()] | nil

Fetch the cached device list for a JID (or user string). Returns nil on a miss or when the entry has expired.

get_many(conn, jids)

@spec get_many(Amarula.Conn.t(), [String.t()]) ::
  {%{required(String.t()) => [device()]}, [String.t()]}

Look up several users at once. Returns {hits, misses} where hits is a user => devices map and misses is the list of users with no fresh entry.

put(conn, jid, devices)

@spec put(Amarula.Conn.t(), String.t(), [device()]) :: :ok | {:error, term()}

Store devices for user (overwriting any prior entry). devices is the full list for that user; pass the user JID or bare user string.

put_many(conn, by_user)

@spec put_many(Amarula.Conn.t(), %{required(String.t()) => [device()]}) :: :ok

Store device lists for many users at once. by_user is user => devices. Returns :ok.