Skip to main content

Watch

Watch delivers a push-based stream of change events. Instead of polling for updates, you register a handler and the store calls it whenever a watched key changes. Use watch to build event-driven and reactive workflows.

Why watch matters

Polling wastes work: you repeatedly ask "has this changed yet?", burn resources on unchanged reads, and still react late because a change can land just after you checked.

Watch inverts this. You subscribe once, and the store pushes an event the moment data changes. You react immediately and stop polling entirely.

How watch works

You subscribe with a handler delegate. The store invokes your handler for each change to a watched key, and the subscription stays active until you stop it. You choose the scope: a single key or a key prefix.

note

The watch surface lives on IZaris, which extends IZarisClient with the Watch accessor. IZarisClientProvider.GetAsync is typed to return IZarisClient, so obtain the client as IZaris before calling client.Watch:

var client = (IZaris)await provider.GetAsync("default");

Watch a single key

WatchKeyAsync subscribes to one key. It returns a tuple: the subscription handle you use to stop later, and the initial record captured at subscription time.

var (subscription, initialRecord) = await client.Watch.WatchKeyAsync(
"order:1001",
new WatchOptions { IncludeInitialSnapshot = true },
ev =>
{
Console.WriteLine($"{ev.EventType}{ev.Key} = {ev.Value}");
});

When IncludeInitialSnapshot is true, you receive the current value first and then the live stream of changes. The flow is: the key already exists, you start watching, you get the current value, and future updates stream in. When it is false, you receive only changes that happen after you subscribe.

tip

Enable IncludeInitialSnapshot whenever your handler needs the current state to be correct, such as building a local cache. Without it, your handler sees nothing until the next write, so it starts from an empty or stale picture of the data.

Watch a prefix

WatchPrefixAsync subscribes to every key that starts with a prefix. Use it to follow a whole group of related keys with one subscription.

var subscription = await client.Watch.WatchPrefixAsync(
"orders:",
new WatchOptions { IncludeInitialSnapshot = false },
ev =>
{
Console.WriteLine($"{ev.Key} changed");
});

Event model

Your handler receives a WatchEvent for each change. It carries the following fields.

  • Key — the key that changed.
  • Value — the new value, when applicable.
  • EventType — a WatchEventType, either Put (created or updated) or Delete (removed).
  • Revision — the change version, which increases with each change.

Branch on EventType to handle creates and updates separately from deletes.

await client.Watch.WatchPrefixAsync(
"jobs:",
null,
ev =>
{
if (ev.EventType == WatchEventType.Put)
{
Console.WriteLine($"Job updated: {ev.Key}");
}
});

Stop a subscription

Watch returns a subscription handle. Call StopAsync when you no longer need the events. This stops delivery and releases the resources the subscription holds.

await subscription.StopAsync();
warning

A subscription runs until you stop it. If you drop the handle without calling StopAsync, the subscription keeps consuming resources for the life of the client. Stop every subscription you start, and stop it in a finally block or on cancellation so an exception cannot leak it.

Delivery behavior

Watch scopes events precisely and runs continuously, but you should understand what it does and does not promise.

  • Events are scoped to what you watch. A key watch fires only for that key; a prefix watch fires only for matching keys. Unrelated keys never reach your handler.
  • Watchers are isolated from one another. A watcher on prefix a: and a watcher on prefix b: each receive only their own events; they do not interfere. A handler that throws is caught in isolation, so one failing watcher never stops delivery to the others.
  • Each change is delivered on its own. The server does not coalesce or conflate updates: every Put and Delete to a watched key produces its own event while the subscription is connected.
  • The stream is continuous. Watch is designed for long-running subscriptions that stay open until you stop them.
warning

There is no server-side replay buffer, so watch does not redeliver events that occurred while the client was disconnected — a change is delivered to a live subscription or not at all. Treat the value you get as the latest known state rather than a guaranteed gap-free history across a reconnect: re-subscribe with IncludeInitialSnapshot to re-establish current state, and use the Revision field, which increases with each change, to order the events you receive.

Thread safety

Your handler may be invoked concurrently. If it touches shared state, synchronize that access yourself.

lock (events)
{
events.Add(ev);
}

Keep handlers lightweight and non-blocking. A handler that runs slow work or blocks holds up event processing, so offload heavy work to a queue or background task and return quickly.

When to use watch

Watch fits any case where you need to react to data changes instead of polling for them. Common uses include the following.

  • Job processing — watch for new jobs and process them the moment they appear.
  • Cache invalidation — watch keys and refresh a local cache when they change.
  • Distributed coordination — react to lease or lock changes to trigger failover.
  • Presence tracking — watch active workers to detect joins and leaves.

Next steps