Summary
JasperFx/jasperfx#564 adds a new async daemon knob that needs documentation in the projections/daemon docs:
opts.Projections.StopAndDrainTimeout = 30.Seconds(); // default is 5 seconds
DaemonSettings.StopAndDrainTimeout (also on IReadOnlyDaemonSettings) bounds how long the daemon waits for a single subscription or projection shard to gracefully finish its in-flight page and flush its progression when that agent is stopped. It applies to all three stop paths: StopAgentAsync, StopAllAsync (the SIGTERM/shutdown path), and the internal stop-if-running agent replacement. The default is 5 seconds, which is exactly what was hardcoded before, so nothing changes unless it's configured.
What the docs should say
- What it bounds.
StopAndDrainAsync is the graceful path — it lets the in-flight page/batch complete and flushes the progression row. Exceeding the timeout cancels that drain mid-flight, which abandons the progression flush.
- Why you'd raise it. If the drain is cut off before progression is flushed, the shard restarts against a stale progression row and throws
ProgressionProgressOutOfOrderException on the next start. Raise it when in-flight batches legitimately take longer than 5 seconds: large BatchSize, expensive projections, heavy rebuild load, or a slow/contended database. This is most visible on shutdown of a host with a large agent universe — e.g. database-per-tenant with thousands of (projection × tenant) shards draining inside a Kubernetes termination grace window (the motivating incident behind jasperfx#564).
- Pair it with the host's shutdown grace period. The per-shard bound is only useful if the process actually lives long enough to use it, so a raised value should be matched by
HostOptions.ShutdownTimeout and the Kubernetes terminationGracePeriodSeconds.
- Why you'd lower it. A host that would rather cut a wedged shard loose quickly and take the progression replay hit — for instance to keep node-failover/reassignment latency low — can set it below the default.
- Opting out.
Timeout.InfiniteTimeSpan, or any non-positive value, removes the separate bound; the drain is then limited only by the daemon's own cancellation. Worth calling out that this means a genuinely wedged shard can hold up shutdown indefinitely.
Notes
- Docs only — no code change needed here; the setting arrives with the JasperFx.Events version carrying jasperfx#564.
Summary
JasperFx/jasperfx#564 adds a new async daemon knob that needs documentation in the projections/daemon docs:
DaemonSettings.StopAndDrainTimeout(also onIReadOnlyDaemonSettings) bounds how long the daemon waits for a single subscription or projection shard to gracefully finish its in-flight page and flush its progression when that agent is stopped. It applies to all three stop paths:StopAgentAsync,StopAllAsync(the SIGTERM/shutdown path), and the internal stop-if-running agent replacement. The default is 5 seconds, which is exactly what was hardcoded before, so nothing changes unless it's configured.What the docs should say
StopAndDrainAsyncis the graceful path — it lets the in-flight page/batch complete and flushes the progression row. Exceeding the timeout cancels that drain mid-flight, which abandons the progression flush.ProgressionProgressOutOfOrderExceptionon the next start. Raise it when in-flight batches legitimately take longer than 5 seconds: largeBatchSize, expensive projections, heavy rebuild load, or a slow/contended database. This is most visible on shutdown of a host with a large agent universe — e.g. database-per-tenant with thousands of (projection × tenant) shards draining inside a Kubernetes termination grace window (the motivating incident behind jasperfx#564).HostOptions.ShutdownTimeoutand the KubernetesterminationGracePeriodSeconds.Timeout.InfiniteTimeSpan, or any non-positive value, removes the separate bound; the drain is then limited only by the daemon's own cancellation. Worth calling out that this means a genuinely wedged shard can hold up shutdown indefinitely.Notes