Malachi.Consumer.CoordinatorRouter (malachi v0.22.7)

View Source

Routes a topic's consumer-group coordination to a single owning node, so every member of a group: connected to any broker: reaches the same GroupCoordinator. The invariant "each range under exactly one member" needs one coordination authority per topic; with a local coordinator per node, members on different nodes would see divergent assignments.

Mirrors NorthGuard's request routing: a broker consults its local view of the sharded metadata (the HashRing over vnodes) to find the vnode that owns the topic, then forwards to the node currently leading that vnode's Raft cluster (the owner co-locates with the topic's metadata, so the coordinator's active_range_ids/2 resolves against the local broker).

Single-node / in-memory has no sharded control plane → no topology → everything is :local, so the behavior is exactly the pre-routing one. The routing decision is a pure function (location/4) with the topology and live leadership passed in; resolve/2 is the thin runtime wrapper that reads the boot-published topology (:persistent_term) and the current Raft leader.

Summary

Functions

The per-vnode coordinator name derived from a base_name and a vnode_id. In the sharded control plane each led vnode runs its own GroupCoordinator (one per vnode, on the leader: the NorthGuard model), so routing and the boot wiring must agree on this name.

Decides where a topic's coordination lives. Returns :local when this node owns the topic's vnode: and, fail-safe, whenever there is no topology, the ring can't route, the vnode is unknown, or its leader can't be resolved (a resolution gap degrades to the local coordinator rather than dropping the request). Returns {:remote, node} when another node leads the owning vnode. leader_fn maps a vnode's server id to the node currently leading it (or nil if unknown).

Whether this node owns topic's coordination (it leads the topic's vnode, or there is no sharded topology). The owning coordinator uses this to reject work routed to it by a stale leadership view.

Publishes the static routing topology (the ring + vnode→server-id map) for resolve/2. Called once at boot when the sharded control plane is established; unset means single-node / in-memory.

Turns a location/4 result into a coordinator GenServer.server() ref ({name, node} if remote).

Resolves the coordinator ref for topic. Single-node / in-memory (no topology): the bare base_name. Sharded: the topic's vnode owns a per-vnode coordinator on its leader, coordinator_name/2 locally when this node leads it, or {coordinator_name/2, owner_node} to forward. Falls back to base_name when the owning vnode or its leader can't be resolved (a transient failover state; the caller re-resolves).

The current routing topology, or nil in single-node / in-memory mode (no sharded control plane).

Types

location()

@type location() :: :local | {:remote, node()}

topology()

@type topology() :: %{
  ring: Malachi.Cluster.HashRing.t(),
  servers: %{required(term()) => term()}
}

Functions

coordinator_name(base_name, vnode_id)

@spec coordinator_name(atom(), term()) :: atom()

The per-vnode coordinator name derived from a base_name and a vnode_id. In the sharded control plane each led vnode runs its own GroupCoordinator (one per vnode, on the leader: the NorthGuard model), so routing and the boot wiring must agree on this name.

location(topic, topology, this_node, leader_fn)

@spec location(term(), topology() | nil, node(), (term() -> node() | nil)) ::
  location()

Decides where a topic's coordination lives. Returns :local when this node owns the topic's vnode: and, fail-safe, whenever there is no topology, the ring can't route, the vnode is unknown, or its leader can't be resolved (a resolution gap degrades to the local coordinator rather than dropping the request). Returns {:remote, node} when another node leads the owning vnode. leader_fn maps a vnode's server id to the node currently leading it (or nil if unknown).

owns?(topic)

@spec owns?(term()) :: boolean()

Whether this node owns topic's coordination (it leads the topic's vnode, or there is no sharded topology). The owning coordinator uses this to reject work routed to it by a stale leadership view.

put_topology(ring, servers)

@spec put_topology(Malachi.Cluster.HashRing.t(), %{required(term()) => term()}) :: :ok

Publishes the static routing topology (the ring + vnode→server-id map) for resolve/2. Called once at boot when the sharded control plane is established; unset means single-node / in-memory.

ref(arg1, name)

@spec ref(location(), atom()) :: GenServer.server()

Turns a location/4 result into a coordinator GenServer.server() ref ({name, node} if remote).

resolve(base_name, topic)

@spec resolve(atom(), term()) :: GenServer.server()

Resolves the coordinator ref for topic. Single-node / in-memory (no topology): the bare base_name. Sharded: the topic's vnode owns a per-vnode coordinator on its leader, coordinator_name/2 locally when this node leads it, or {coordinator_name/2, owner_node} to forward. Falls back to base_name when the owning vnode or its leader can't be resolved (a transient failover state; the caller re-resolves).

topology()

@spec topology() :: topology() | nil

The current routing topology, or nil in single-node / in-memory mode (no sharded control plane).