Engine configuration (:core)
These are the :core engine settings — the stream-processing knobs that exist whether you embed stoatflow-core directly or run on the batteries-included stoatflow-runtime. On the runtime they live under the stoatflow: block of application.yaml; in plain :core code you set the same values on StreamsConfig (or its Builder). This page groups the ones you'll actually reach for, with their defaults.
:core knobs exist (dispatch tuning, restoration sizing, RocksDB presets, FK-join, caching, hot-standby HA). For every key with its type and bounds, see Configuration reference. For the layered merge model behind YAML overrides and Kafka client passthrough, see Configuration model and Kafka client config.Identity and connection
Only application-id is required — it has no default, so the engine won't start without it. bootstrap-servers defaults to localhost:9092, which is fine for local development but you'll set it explicitly for any real broker.
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.application-id | applicationId | — (required) | Used for the consumer group and the transactional id. Pick a stable value; changing it starts a fresh consumer group. |
stoatflow.bootstrap-servers | bootstrapServers | localhost:9092 | Comma-separated broker list. Override for any non-local broker. |
stoatflow:
application-id: order-processor
bootstrap-servers: kafka-1:9092,kafka-2:9092,kafka-3:9092
validation.* block in the reference.Default serdes
When an operator doesn't specify a serde via Consumed, Produced, Materialized, etc., these defaults apply. Both default to ByteArray — set them to the type your topology mostly uses.
| YAML key | StreamsConfig field | Default |
|---|---|---|
stoatflow.default-key-serde | defaultKeySerde | Serdes.ByteArray() |
stoatflow.default-value-serde | defaultValueSerde | Serdes.ByteArray() |
In YAML the values are fully qualified serde class names; in code you pass Serde instances:
val config = StreamsConfig.builder("order-processor", "localhost:9092")
.defaultKeySerde(Serdes.String())
.defaultValueSerde(Serdes.String())
.build()
var config = StreamsConfig.builder("order-processor", "localhost:9092")
.defaultKeySerde(Serdes.String())
.defaultValueSerde(Serdes.String())
.build();
stoatflow:
default-key-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
default-value-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
streamsConfigOverrides { ... } in code — see Your first app. Using Schema Registry serdes? Set stoatflow.schema-registry-url; it's propagated to default serdes automatically. More in Serdes.Lanes (parallelism)
Lanes are the engine's virtual partitions — records with the same key always route to the same lane, preserving per-key order while running many keys in parallel. See Lanes and parallelism for the why.
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.lanes.count | numLanes | max(2, CPU cores) | More lanes = more per-key parallelism, at the cost of more queues. Start at the default; I/O-bound workloads commonly go 4×–8× cores — see Tuning. |
stoatflow.lanes.queue-capacity | laneQueueCapacity | 300 | Per-lane queue depth (backpressure). Minimum 32. |
stoatflow.lanes.thread-type | laneThreadType | VIRTUAL | VIRTUAL (Project Loom) or PLATFORM (OS threads). Keep VIRTUAL unless you've switched the RocksDB backend to JNI and pinning is a concern. |
stoatflow:
lanes:
count: 32
queue-capacity: 500
Commit-barrier cadence
The commit barrier is what makes StoatFlow exactly-once: when a barrier flows through the topology, state flushes and the Kafka transaction commits as one atomic unit. The cadence self-tunes at runtime within the bounds you set — interval-ms is the seed, and the engine adapts between min-interval-ms and max-interval-ms. You set the envelope, not the algorithm; how the engine schedules within it is an implementation concern (see Architecture).
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.commit-barrier.interval-ms | barrierIntervalMs | 500 | Seed interval. The runtime adjusts from here. |
stoatflow.commit-barrier.min-interval-ms | barrierMinIntervalMs | 150 | Floor on barrier-to-barrier time. |
stoatflow.commit-barrier.max-interval-ms | barrierMaxIntervalMs | 5000 | Ceiling on barrier-to-barrier time. |
stoatflow.commit-barrier.interval-factor | barrierIntervalFactor | 1.5 | Multiplier applied to recent commit duration when spacing barriers, clamped to [min, max] (>= 1.0). Rarely changed. |
stoatflow.commit-barrier.timeout-ms | barrierTimeoutMs | 60000 | Bounds the commit path. Must be greater than interval-ms. Also cascades to the Kafka producer's transaction.timeout.ms, max.block.ms, and delivery.timeout.ms. |
stoatflow.commit-barrier.max-epoch-records | barrierMaxEpochRecords | unset (no hard cap) | Optional hard cap on records committed per barrier. Leave unset unless you need a strict bound. |
stoatflow:
commit-barrier:
interval-ms: 500
min-interval-ms: 150
max-interval-ms: 5000
timeout-ms: 60000
min-interval-ms and max-interval-ms commits more often (lower end-to-end latency, more transaction overhead). Raising them batches more work per commit (higher throughput, higher tail latency). The defaults are a balanced starting point — tune from observed commit duration. See Tuning.timeout-ms, the commit aborts and the process restarts cleanly. On restart, state recovers to the last committed barrier — no loss, no duplication under exactly-once. The barrier scheduling cadence and the commit protocol's internals are engine concerns; see Architecture for the boundary, and Exactly-once for the guarantee.There are several finer self-tuning knobs under commit-barrier.* (e.g. commit-duration-ema-alpha, initial-max-epoch-records). They have sensible defaults and rarely need changing — the full list is in the reference.
Processing guarantee
Controls the commit protocol. Both modes use barriers for state-flush and offset-commit synchronization; the difference is the transaction boundary.
| YAML key | StreamsConfig field | Default |
|---|---|---|
stoatflow.processing-guarantee | processingGuarantee | EXACTLY_ONCE |
| Value | Behaviour |
|---|---|
EXACTLY_ONCE (default) | Kafka transactions commit output, changelog, and consumer offsets atomically. No duplicates or loss on crash recovery. Higher per-commit cost. |
AT_LEAST_ONCE | Non-transactional producer with consumer.commitSync(). Lower commit latency. Output records may be duplicated on crash recovery; windowed aggregations may over-count. |
stoatflow:
processing-guarantee: EXACTLY_ONCE
See Exactly-once for the full semantics and the at-least-once recovery contract.
State and changelog
State stores are RocksDB-backed by default and durably backed by Kafka changelog topics, so state survives restarts. See State stores and State and thread safety.
State directory and memory
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.state.dir | stateDir | ${java.io.tmpdir}/stoatflow | Put this on fast local storage (SSD). In Kubernetes, mount a PV — the default temp dir is wiped on pod restart, forcing full restoration. |
stoatflow.state.uncommitted-max-bytes | stateStoreUncommittedMaxBytes | 268435456 (256 MB) | Global cap on buffered uncommitted state; exceeding it triggers an early barrier. |
stoatflow.state.segment-checkpoint-records | segmentCheckpointRecords | 100000 | Periodic checkpoint cadence (record count) for segmented window/session stores, which run WAL-off and would otherwise be durable only on close(). Bounds how much changelog an ungraceful restart — or a hot-standby promotion — re-applies. Whichever-first with segment-checkpoint-ms. |
stoatflow.state.segment-checkpoint-ms | segmentCheckpointMs | 30000 (30 s) | The same checkpoint cadence by wall-clock time; whichever-first with segment-checkpoint-records. |
stoatflow.rocks-db.preset | rocksDbConfig | DEFAULT (256 MB) | DEFAULT / LOW_MEMORY (64 MB) / HIGH_PERFORMANCE (1 GB) total RocksDB memory. |
stoatflow.rocks-db.backend | rocksDbBackendType | AUTO | AUTO picks FFM (Foreign Function & Memory — no virtual-thread pinning) on the JVM and JNI under a native image; set FFM or JNI to force one. |
stoatflow:
state:
dir: /var/lib/stoatflow/state
uncommitted-max-bytes: 268435456
rocks-db:
preset: DEFAULT
Changelog topics
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.changelog.enabled | changelogEnabled | true | When on, state mutations are written to changelog topics inside the same transaction. |
stoatflow.changelog.create-topics-if-not-exist | createChangelogTopicsIfNotExist | true | Auto-creates compacted changelog topics on startup. |
stoatflow.changelog.replication-factor | changelogReplicationFactor | -1 (broker default) | Set explicitly (e.g. 3) for production durability. |
stoatflow.changelog.num-partitions | changelogNumPartitions | 1 | One partition is usually sufficient for single-instance. |
stoatflow.state.restoration-enabled | stateRestorationEnabled | true | Restore state from changelog on startup when local state is missing. |
stoatflow:
changelog:
enabled: true
replication-factor: 3
changelog.replication-factor to at least 3 and put state.dir on a persistent volume — together they keep restoration fast (delta, not full) across pod restarts. See Kubernetes and the Production checklist.Event-time and watermarks
Watermarks track event-time progress for windowing, late-record handling, and event-time timers. The engine auto-detects whether your topology needs event-time tracking and bypasses it on the hot path when it doesn't. See Event time and watermarks.
| YAML key | StreamsConfig field | Default | Notes |
|---|---|---|---|
stoatflow.watermark.max-out-of-orderness-ms | maxOutOfOrderness | 10000 (10 s) | How far out of order events may arrive before being treated as late. |
stoatflow.watermark.idleness-timeout-ms | watermarkIdlenessTimeout | 300000 (5 min) | Partitions idle this long are excluded from the global watermark. 0 disables. |
stoatflow.watermark.auto-interval-ms | autoWatermarkIntervalMs | 200 | Periodic watermark emission interval. |
stoatflow.processing.timestamp-coordination-mode | timestampCoordinationMode | AUTO | AUTO (derive from topology), ENABLED (force on), DISABLED (force off, benchmarking). |
stoatflow.processing.max-task-idle-ms | maxTaskIdleMs | 0 | Cross-partition timestamp-ordering wait. -1 disables (max throughput); 0 waits only while the broker has data; >0 adds extra wait. Mirrors KS max.task.idle.ms. |
stoatflow:
watermark:
max-out-of-orderness-ms: 10000
idleness-timeout-ms: 300000
processing:
timestamp-coordination-mode: AUTO
timestamp-coordination-mode: AUTO is correct for almost all topologies — override it only for debugging or benchmarking. Per-source watermark behaviour is configured on Consumed with a WatermarkStrategy; these settings are the engine-wide defaults.Putting it together
A representative production stoatflow: block:
stoatflow:
application-id: order-processor
bootstrap-servers: kafka-1:9092,kafka-2:9092,kafka-3:9092
default-key-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
default-value-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
processing-guarantee: EXACTLY_ONCE
lanes:
count: 32
queue-capacity: 500
commit-barrier:
interval-ms: 500
min-interval-ms: 150
max-interval-ms: 5000
timeout-ms: 60000
state:
dir: /var/lib/stoatflow/state
uncommitted-max-bytes: 268435456
changelog:
enabled: true
replication-factor: 3
watermark:
max-out-of-orderness-ms: 10000
idleness-timeout-ms: 300000
Next steps
- Configuration reference — every
:corekey with type, default, and bounds. - Runtime config (
) — the runtime.*block (HTTP, metrics, endpoints, health). - Kafka client config — passthrough consumer/producer properties and the layered merge order.
- Defaults and presets — the opinionated defaults and how to override them.
- Tuning — picking lane count, barrier cadence, and memory caps from observed behaviour.
How configuration works
How the StoatFlow runtime loads configuration — application.yaml on the classpath, overlay files, environment variables, system properties, and the layered precedence order.
Runtime configuration (:runtime)
The runtime.* section of application.yaml — HTTP server, metrics, info/config endpoints, health checks — plus logging levels and Kafka client passthrough.