Pause and resume
Pause stops the instance from dispatching new records and tracking new offsets, while keeping the consumer session alive and the lane threads warm — so you can resume instantly. It's a runtime control for short operational gaps (a maintenance window on a downstream system, transient backpressure), not a substitute for shutting the instance down.
What pause does
Calling pause moves the instance through two states:
DRAINING— record dispatch stops immediately. No new records are handed to processing; timers and punctuators are paused; records already consumed but not yet dispatched are held, not dropped. Work already in flight finishes, and a final commit flushes that work and its offsets. This is a graceful drain — nothing in flight is dropped.PAUSED— reached automatically once the in-flight work has drained and committed. The instance now sits idle: the Kafka consumer keeps polling for heartbeats (so the group membership and session stay alive), processing threads stay warm and ready, but no new records are consumed and no new offsets are tracked.
Because the consumer keeps heartbeating, pausing does not trigger a consumer-group rebalance and does not require a restart. Resume is near-instant — the threads never went cold.
RUNNING flow and resumes processing — there is no "stays paused across restarts" behaviour.What unpause does
Unpause returns the instance to RUNNING:
- Record dispatch resumes — any held records drain back into processing and consumption continues.
- Timers and punctuators resume.
You can call unpause from either PAUSED (resume after the drain finished) or DRAINING (cancel the drain that's still in progress and resume immediately).
Pause and readiness
This is the key operational interaction. While the instance is DRAINING or PAUSED:
| Probe | State | Meaning |
|---|---|---|
/health/live | UP | The process is healthy — do not restart it. |
/health/ready | DOWN | The instance is intentionally not taking traffic. |
Liveness staying UP is deliberate: a paused instance is working as intended, so an orchestrator must not kill and restart it. Readiness going DOWN reflects that it isn't processing. See Health checks and Probes for how Kubernetes consumes these.
The endpoints
Both endpoints are POST and take no body. They're served by the runtime's HTTP server (runtime.http.enabled, default port 8080).
POST /pause
curl -X POST localhost:8080/pause
| Response | Code | Body |
|---|---|---|
| Pause initiated | 200 | {"status":"pausing","state":"DRAINING"} |
| Wrong state | 400 | {"error":"Cannot pause from state ..."} |
| Not initialized yet | 503 | {"error":"StoatFlow not initialized"} |
Pause is only valid from RUNNING. Calling it during startup, while already paused, or while shutting down returns 400. The 200 response reports DRAINING — the instance reaches PAUSED a moment later once the drain completes; poll /state to confirm.
POST /unpause
curl -X POST localhost:8080/unpause
| Response | Code | Body |
|---|---|---|
| Resumed | 200 | {"status":"resumed","state":"RUNNING"} |
| Wrong state | 400 | {"error":"Cannot unpause from state ..."} |
| Not initialized yet | 503 | {"error":"StoatFlow not initialized"} |
Unpause is valid from PAUSED or DRAINING. Calling it from RUNNING (or any other state) returns 400.
A maintenance sequence
A typical "pause, do something, resume" loop using the HTTP API:
# 1. Pause and watch it settle into PAUSED
curl -X POST localhost:8080/pause
# -> {"status":"pausing","state":"DRAINING"}
# 2. Wait until it's fully drained (readiness flips DOWN; /state shows PAUSED)
until curl -s localhost:8080/state | grep -q '"PAUSED"'; do sleep 1; done
# ... perform maintenance on the downstream system ...
# 3. Resume
curl -X POST localhost:8080/unpause
# -> {"status":"resumed","state":"RUNNING"}
The current lifecycle state is always available at /state.
Programmatic control (core)
The same pause/drain semantics are exposed as methods on the core StoatFlow handle — useful when you embed the engine via :core directly rather than driving it over HTTP. pause() returns once the drain has been initiated (state DRAINING); use awaitState(...) to block until PAUSED.
import io.stoatflow.core.StoatFlow
import java.time.Duration
stoatflow.pause()
stoatflow.awaitState(StoatFlow.State.PAUSED, Duration.ofSeconds(30))
// ... perform maintenance ...
stoatflow.unpause()
import io.stoatflow.core.StoatFlow;
import java.time.Duration;
stoatflow.pause();
stoatflow.awaitState(StoatFlow.State.PAUSED, Duration.ofSeconds(30));
// ... perform maintenance ...
stoatflow.unpause();
pause() throws IllegalStateException if the instance is not RUNNING; unpause() throws if it is not PAUSED or DRAINING — the same state rules the HTTP handlers turn into 400 responses.
:runtime (StoatFlowRuntime), pause and resume are driven over HTTP via /pause and /unpause. The programmatic pause() / unpause() / awaitState() methods above are on the underlying StoatFlow engine handle, which you hold directly when using the :core module.When to use it
- Downstream backpressure. A sink the topology writes to (a database, an external API, another topic's consumer) goes unavailable or slows down. Rather than letting failures pile up, pause, let the downstream recover, then unpause — the consumer session stays alive throughout, so you avoid a rebalance and a cold restart.
- Short maintenance windows. You need to touch the broker, the downstream store, or the host briefly and want a clean, committed boundary. Pause drains and commits in-flight work first, so you resume from a consistent point with no partial in-flight state.
For anything longer-lived or anything that changes the topology or configuration, stop the instance and start it again — pause is for keeping a single instance warm across a brief, deliberate gap, not for long outages. See Production checklist for operational guidance.
Next steps
- REST API — the full endpoint reference, including
/state. - Health checks — how
DRAINING/PAUSEDmap to liveness and readiness. - Probes — wiring the probes into Kubernetes so a paused instance isn't restarted.
Metrics
Scrape StoatFlow's Prometheus /metrics endpoint — Micrometer integration, the key meters the engine emits, license metrics, and common tags.
Plugins and lifecycle hooks
Extend the StoatFlow runtime with custom metrics, HTTP endpoints, and health indicators via the RuntimePlugin interface and lifecycle listeners.