Skip to main content

Service Discovery

Kahuna can store service instance metadata so clients and workers can discover healthy endpoints without running a separate registry.

Use a bucket per service:

services/{service-name}/{instance-id}

Examples:

services/payments/node-a
services/payments/node-b
services/search/node-c

Register an Instance

Use an expiration so the instance disappears if the process dies or stops refreshing its heartbeat.

set `services/payments/node-a` '{"host":"10.0.1.15","port":8080,"version":"1.2.3","zone":"us-east-1a"}' ex 30000 norev

From the .NET client:

await client.SetKeyValue(
"services/payments/node-a",
"""{"host":"10.0.1.15","port":8080,"version":"1.2.3","zone":"us-east-1a"}""",
expiryTime: 30000,
flags: KeyValueFlags.SetNoRevision,
durability: KeyValueDurability.Persistent
);

Refresh the Heartbeat

Refresh the key periodically before the expiration passes:

await client.ExtendKeyValue(
"services/payments/node-a",
TimeSpan.FromSeconds(30),
KeyValueDurability.Persistent
);

If instance metadata can change on heartbeat, write the full value again with the same expiration:

await client.SetKeyValue(
"services/payments/node-a",
currentMetadataJson,
expiryTime: 30000,
flags: KeyValueFlags.SetNoRevision,
durability: KeyValueDurability.Persistent
);

Discover Instances

Read all instances in the service bucket:

get by bucket `services/payments`

From the .NET client:

List<KahunaKeyValue> instances = await client.GetByBucket(
"services/payments",
KeyValueDurability.Persistent
);

foreach (KahunaKeyValue instance in instances)
Console.WriteLine($"{instance.Key}: {instance.ValueAsString()}");

Audit Historical Membership

If you need to inspect which instances were visible at a previous point in time, use an AS OF read:

get by bucket `services/payments` as of 1718392012345

Or use the .NET client snapshot parameter:

List<KahunaKeyValue> previous = await client.GetByBucket(
"services/payments",
KeyValueDurability.Persistent,
snapshotMs: 1718392012345
);

Do not use NOREV for registrations when historical membership is required. No-revision writes keep only the latest value and skip the archived revision records needed by historical reads.

Choosing Durability

Use persistent durability when discovery state must survive node restarts and remain available during failover.

Use ephemeral durability when registrations are high-volume, short-lived, and safe to rebuild from running services.

eset `services/payments/node-a` '{"host":"10.0.1.15","port":8080}' ex 30000
eget by bucket `services/payments`

Operational Notes

  • Set the expiration to several heartbeat intervals, not exactly one interval.
  • Use no-revision writes for high-churn persistent registrations when only the current membership matters.
  • Store enough metadata for clients to make routing decisions without another lookup.
  • Keep service membership under a shared bucket so reads are consistent and partition-local.
  • For large ordered registries, use key-range sharding instead of treating one bucket as permanently single-partition.