Routes SuperCache operations to the correct primary node and applies replication after each write.
Routing contract
- Determine the partition order (integer index) for the operation from
the data tuple or explicit partition argument via
Partition.get_partition_order/1. - Look up
{primary, replicas}fromManager.get_replicas/1(zero-cost:persistent_termread). - If
node() == primary→ apply locally, then callReplicator.replicate/3. - Otherwise → forward the entire operation to the primary via
:erpc, which applies and replicates it. Forwarded calls never forward again (detected via a:forwardedflag in opts) to prevent cycles.
Anti-cycle guard
Every outbound :erpc call appends forwarded: true to its opts list.
A function that receives forwarded: true always executes locally and
skips the primary check, preventing infinite forwarding chains when the
partition map is momentarily inconsistent.
No anonymous functions across node boundaries
All :erpc calls pass only plain, serializable Erlang terms — integers,
atoms, and tuples. Anonymous functions (closures) are never passed via
:erpc because Erlang fun serialization is fragile: the remote node must
have the identical module version, otherwise the call raises badfun.
Instead, every remote read goes through the explicit public dispatcher
local_read/3, which takes an operation atom (:get | :match | :match_object)
and a plain argument.
3PC writes
When Manager.replication_mode/0 returns :strong, writes are handed
to ThreePhaseCommit.commit/2 on the primary instead of the normal
local-write + async/sync replicate path.
Read-your-writes consistency
When a process writes a key, the Router records the partition order in a
per-process ETS table. Subsequent reads of the same partition (within a
configurable TTL) are automatically routed to the primary node, ensuring
the reader sees its own writes even in :local read mode.
The tracking table is cleaned up lazily — entries older than the TTL are
pruned on each write. This adds negligible overhead (~100ns per write)
while providing strong read-your-writes guarantees without requiring
read_mode: :primary on every call.
Summary
Functions
Route a key-based delete to the correct primary.
Delete all records — one routed call per partition.
Route a delete by explicit key + partition value to the correct primary.
Route a match-based delete, one partition order at a time.
Route a key-based get.
Route a get by explicit key + partition value.
Route a match-pattern scan across one or all partitions.
Route a match-object scan across one or all partitions.
Route a put to the correct primary, then replicate.
Route a batch of puts to the correct primary, then replicate.
Fold over local ETS — always local, never forwarded.
Functions
Route a key-based delete to the correct primary.
@spec route_delete_all() :: :ok
Delete all records — one routed call per partition.
Route a delete by explicit key + partition value to the correct primary.
Route a match-based delete, one partition order at a time.
Route a key-based get.
Route a get by explicit key + partition value.
Route a match-pattern scan across one or all partitions.
Route a match-object scan across one or all partitions.
Route a put to the correct primary, then replicate.
Route a batch of puts to the correct primary, then replicate.
Groups data by partition order and sends each group in a single :erpc call,
dramatically reducing network overhead for bulk writes.
Example
Router.route_put_batch!([
{:user, 1, "Alice"},
{:user, 2, "Bob"},
{:session, "tok1", :active}
])
Fold over local ETS — always local, never forwarded.