Amarula.RetryCache.ETS (amarula v0.4.0)
View SourceIn-memory Amarula.RetryCache adapter — the default.
One :public, named ETS table per connection profile, holding
{msg_id, entry}. State is lost on restart, which is acceptable: a retry
receipt arrives within seconds of the original send.
Bound
A message only needs to be held for as long as a retry could still arrive for it. The protocol bounds that two ways, and this cache mirrors both (matching the reference implementation):
- TTL —
@ttl_ms(5 min). A retry receipt normally lands within seconds; after a few minutes it won't come (and the message is stale anyway). Entries older than the TTL (byentry.ts) are dropped on the next write. - Hard cap —
:max_entries(default 512). A safety ceiling on memory; past it, the oldest entries are evicted. Size this to your peak sends per ~5 min so a burst can't push a still-unacked message out before its retry arrives.
If you already persist your sent messages, consider a read-only custom adapter
pointed at your own store instead — see Amarula.RetryCache ("Using your own
message store").
Ownership
The table is created by ensure_local/2, called from Connection.init, so it
is owned by the Connection process and named by profile. Because the table
dies with its owner, a Connection crash/restart recreates it empty — so a
poisoned entry can never outlive the restart it triggers (no crash-loop on a
bad cached value).
It must be :public, not :protected: it is written from two processes —
Connection itself (on a retry receipt) and each per-recipient
ConversationSender (the Amarula.RetryCache.Step in the send pipe records the
sent message there) — while ownership (and thus the restart-clearing lifetime
above) stays with Connection.
Options
:max_entries— hard cap before oldest-eviction (default 512).
Summary
Functions
Create the profile's table, owned by the calling process (Connection). Called
from Connection.init before any reader, so we never create lazily on first
use — no create race, no rescue. Idempotent.