Skip to main content

Client Leader-Aware Routing

Kahuna accepts requests on every node. If a request lands on a node that does not lead the target partition, that node forwards the request to the current owner. Client leader-aware routing removes many of those extra hops by letting the client learn where a resource lives and send later requests there directly.

Routing is advisory. The server still re-resolves the key, lock, or sequence when the request arrives, checks live range fences, and verifies leadership. A stale route costs a forward or retry, not a wrong result.

Modes

Set KahunaOptions.Routing in .NET or routing in TypeScript:

ModeBehavior
AutoDefault. Uses Learned when the client has several endpoints and RoundRobin when it has one.
RoundRobinRotates over configured endpoints. This is the pre-routing behavior.
LearnedReuses the endpoint reported by a previous response for the same resource. Unknown resources fall back to rotation.
MetadataUses learned routes and also reads cluster routing metadata so unseen resources can route directly.
using Kahuna.Client;
using Kahuna.Client.Routing;

var client = new KahunaClient(
[
"https://node1:8082",
"https://node2:8084",
"https://node3:8086"
],
options: new KahunaOptions
{
Routing = KahunaRoutingMode.Metadata
}
);
import { KahunaClient } from "kahuna-client";

const client = new KahunaClient({
endpoints: [
"https://node1:8082",
"https://node2:8084",
"https://node3:8086"
],
routing: "metadata"
});

Auto is usually the right starting point. A single-endpoint client stays on RoundRobin because it cannot use hints that name other nodes unless those endpoints are also configured or mapped.

What Routes

OperationRouting behavior
Point key/value operationsRouted by key after a route is learned or resolved from metadata.
Lock operationsRouted by lock resource in a separate routing domain from key/value keys.
Sequence operationsRouted by the sequence storage key rule.
Batch point operationsServer-dispatched, while per-item routes are learned from the response.
Prefix, bucket, and range scansServer-dispatched because one coordinator owns fan-out, pagination, and merge.
Transaction scripts and sessionsRouted by coordinator identity, not by an individual data key.
Cluster, range, backup, and snapshot administrationNode-scoped; explicit nodeUrl arguments are honored.

Batch calls intentionally stay server-dispatched. Splitting one batch into several client requests would make transport failures ambiguous per group, while the server already returns per-item outcomes and route hints.

Server Advertisement

Servers include advisory route hints in REST and gRPC responses by default. Configure what endpoint a node advertises with:

FlagMeaning
--advertised-client-endpointBase URL this node tells clients to dial. Empty derives it from the Raft endpoint and advertised scheme.
--advertised-client-schemeScheme prepended to peer Raft endpoints when deriving peer client URLs. Empty follows --raft-grpc-scheme.
--disable-peer-endpoint-advertisementDo not name peer nodes in hints. Use this when peer client URLs cannot be derived from Raft endpoints.
--disable-routing-hintsReturn no routing hints. Clients keep their configured endpoint selection.

The default derived endpoint is:

<advertised scheme><raft host>:<raft port>

Set --advertised-client-endpoint explicitly when clients reach a node through a different host or port than the cluster uses internally, such as container port mapping or separate internal and external DNS names.

In MutualTls deployments, routing hints must name application listeners, not the mTLS cluster listener. Set --advertised-client-endpoint on each node and use --disable-peer-endpoint-advertisement unless every peer application URL can be derived safely. A normal client cannot follow a hint that points at a listener requiring node certificates.

Endpoint Mapping

A response cannot make the client dial an arbitrary address by default. A hint is accepted only when it resolves to a configured endpoint.

Use RoutingEndpointMap when servers advertise internal addresses but the application dials external addresses:

var client = new KahunaClient(
[
"https://localhost:8082",
"https://localhost:8084",
"https://localhost:8086"
],
options: new KahunaOptions
{
Routing = KahunaRoutingMode.Learned,
RoutingEndpointMap = new Dictionary<string, string>
{
["https://172.30.0.2:8082"] = "https://localhost:8082",
["https://172.30.0.3:8084"] = "https://localhost:8084",
["https://172.30.0.4:8086"] = "https://localhost:8086"
}
}
);
const client = new KahunaClient({
endpoints: [
"https://localhost:8082",
"https://localhost:8084",
"https://localhost:8086"
],
routing: "learned",
routingEndpointMap: {
"https://172.30.0.2:8082": "https://localhost:8082",
"https://172.30.0.3:8084": "https://localhost:8084",
"https://172.30.0.4:8086": "https://localhost:8086"
}
});

Set AllowUnlistedRoutingEndpoints = true only when every advertised node URL is trusted and client-reachable, including nodes added after the client starts.

Metadata Mode

Metadata mode reads GET /v1/cluster/routing or the equivalent gRPC Cluster.GetRoutingMetadata call. The map includes:

  • hash-routing rules for normal key spaces, including placement groups
  • key-range descriptors and generations for range-routed key spaces
  • sequence storage-key routing rules
  • advisory partition leaders

The current hash algorithm identifier is kahuna.placement-group-jump-xxh32-v1. It means the client hashes the key space before the last /, then reduces that key space to the placement group before the first |. Key spaces that share a placement group, such as orders|rows/... and orders|by_customer/..., route to the same hash partition.

The metadata read is not on the critical path of an operation. If no usable map is available, the operation goes out through learned routing or rotation and the refreshed map helps later operations.

Clients refuse metadata they cannot interpret exactly, including unknown schema versions, unknown hash algorithms or separators, incoherent range snapshots, missing leaders, or key-range gaps. In those cases the client falls back instead of guessing.

Cache and Metrics

Relevant client options:

OptionDefaultMeaning
RouteCacheCapacity4096Learned route entries kept in memory.
RouteHintLifetime60 secondsMaximum age of a learned route before it must be observed again.
RoutingEndpointCooldown5 secondsTime an endpoint is skipped after a transport failure.
RoutingMetadataLifetime60 secondsMetadata map lifetime in Metadata mode.

The client publishes routing counters under the Kahuna.Client.Routing meter:

CounterMeaning
cache_hits and cache_missesLearned-route cache outcomes.
metadata_hitsOperations routed from the metadata map.
hints_learnedAccepted response hints.
hints_rejectedDropped hints, tagged by reason.
endpoints_suppressedEndpoints put into failure cooldown.
suppressed_routes_skippedCached routes skipped because their endpoint was cooling down.
metadata_refreshesMetadata reads issued.
metadata_refresh_failuresMetadata reads that produced no usable map.

If hints_rejected with reason=endpoint_rejected climbs while cache hits stay low, the server is advertising endpoints the client was not configured or mapped to dial.

Measured Effect

On a local three-node cluster with cleartext gRPC, in-memory storage, 12 partitions, 2,000 hot keys, and 64 concurrent get workers, learned routing reached 116,808 requests per second. The same benchmark with round-robin endpoint selection reached 72,188 requests per second, so removing the extra forwarding hop improved this read-heavy workload by about 62%.

Treat this as a concrete example, not a guaranteed ceiling. The benefit is largest when operations repeat resources and the client can reuse learned routes. Workloads dominated by writes still pay the Raft replication cost, and very large key spaces may need Metadata mode or a larger RouteCacheCapacity to keep hit rates high.

Rollout

  1. Upgrade servers. Route hints are additive and older clients ignore them.
  2. Confirm what a node advertises with GET /v1/cluster/routing.
  3. Configure --advertised-client-endpoint or RoutingEndpointMap if advertised URLs differ from dialed URLs.
  4. Upgrade clients. A multi-endpoint client in Auto starts using learned routing.
  5. Use Routing = RoundRobin for clients that should keep the old endpoint-selection behavior.