Skip to content

Observers

Observer configuration controls retry behavior, timeouts, watchdog monitoring, and how events fan out to scaled-out client instances.

{
"observers": {
"subscriberTimeout": 30,
"maxRetryAttempts": 10,
"backoffDelay": 1,
"exponentialBackoffDelayFactor": 2,
"maximumBackoffDelay": 600,
"quarantineOnFailedPartitionCount": 0,
"quarantineOnFailedPartitionPercentage": 0.0,
"watchdogInterval": 60,
"fanOutStrategy": "round-robin"
}
}
PropertyTypeDefaultDescription
subscriberTimeoutnumber30How many seconds an observer waits for its subscriber to answer a batch before giving up on it. 0 waits indefinitely. See Subscriber timeout
maxRetryAttemptsnumber10Maximum retry attempts for failed partitions (0 = infinite)
quarantineOnFailedPartitionCountnumber0Quarantine the observer once this many of its partitions have failed (0 = never)
quarantineOnFailedPartitionPercentagenumber0.0Quarantine the observer once this share of its observed partitions have failed (0.0 = never)
backoffDelaynumber1Initial backoff delay in seconds
exponentialBackoffDelayFactornumber2Exponential backoff multiplier
maximumBackoffDelaynumber600Maximum backoff delay in seconds
watchdogIntervalnumber60Interval in seconds between watchdog checks; the watchdog verifies connected clients are still active, running jobs (replay and catch-up) are still progressing, and NextEventSequenceNumber is up-to-date
fanOutStrategystringround-robinStrategy for distributing events across multiple connected instances of the same client. round-robin distributes deterministically by partition key, keeping every partition sticky to one instance and preserving per-partition ordering. random picks a random instance per delivery

Every path that hands events to an observer’s subscriber — live delivery, catch-up and replay — waits at most subscriberTimeout seconds for an answer. When it elapses, the batch is recorded as a failed partition of kind Timeout and the partition retries with the usual backoff.

Two things are worth knowing before changing it:

  • Giving up abandons the wait, not the work. The subscriber is a grain and keeps processing the batch it was handed; the events are simply redelivered when the partition retries. Observers are expected to be idempotent, so that is safe — but it does mean a short timeout can have a subscriber working on a batch the kernel has already written off.
  • Raising it past the transport’s own response timeout has no effect, because that one gives up first. The default deliberately matches it, so the setting bounds nothing new until you lower it. Lowering it is the point: a subscriber that should always answer in a second or two gets its partition back into retry quickly instead of holding a job step for half a minute.

Set it to 0 to wait indefinitely — the escape hatch for a subscriber whose work legitimately has no upper bound.

Every attempt recorded against a failed partition carries a kind, so an observer that is wrong can be told apart from one that was only waiting on a busy kernel:

KindMeaning
HandlingThe subscriber failed while handling the events. This is the failure that means something is wrong
TimeoutThe call to the subscriber did not come back in time. The events were never rejected — the kernel ran out of patience waiting, which says the system was congested
DisconnectedThe subscriber was gone by the time the events reached it
UnknownNothing classified the failure. Every attempt recorded before failures carried a kind reads back as this

A partition whose last attempt is a Timeout does not count toward the quarantine thresholds above. Quarantining stops retries and needs an operator to undo, which is the right answer for an observer that is wrong and the wrong answer for one waiting on congestion that will clear on its own.

When multiple instances of the same client application connect, its reactors and reducers all subscribe to the same observer. Chronicle fans event delivery out across the instances using the configured fanOutStrategy. If an instance disconnects, it is removed immediately and its partitions are redistributed to the remaining instances - the observer only unsubscribes when the last instance is gone.