Engine configuration (:core)

The :core engine knobs every StoatFlow app sets — application id, brokers, default serdes, lanes, commit-barrier cadence, state dir, changelog, processing guarantee, and event-time coordination — with defaults from source.

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.

This page covers the settings most apps touch. Many more :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 keyStreamsConfig fieldDefaultNotes
stoatflow.application-idapplicationId— (required)Used for the consumer group and the transactional id. Pick a stable value; changing it starts a fresh consumer group.
stoatflow.bootstrap-serversbootstrapServerslocalhost:9092Comma-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
StoatFlow is single-instance — exactly one process owns all partitions. Startup validates complete partition assignment and (optionally) checks for other live group members. See Lanes and parallelism and the 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 keyStreamsConfig fieldDefault
stoatflow.default-key-serdedefaultKeySerdeSerdes.ByteArray()
stoatflow.default-value-serdedefaultValueSerdeSerdes.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()
stoatflow:
  default-key-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
  default-value-serde: org.apache.kafka.common.serialization.Serdes$StringSerde
On the runtime, the cleaner way to set typed default serdes is 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 keyStreamsConfig fieldDefaultNotes
stoatflow.lanes.countnumLanesmax(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-capacitylaneQueueCapacity300Per-lane queue depth (backpressure). Minimum 32.
stoatflow.lanes.thread-typelaneThreadTypeVIRTUALVIRTUAL (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 keyStreamsConfig fieldDefaultNotes
stoatflow.commit-barrier.interval-msbarrierIntervalMs500Seed interval. The runtime adjusts from here.
stoatflow.commit-barrier.min-interval-msbarrierMinIntervalMs150Floor on barrier-to-barrier time.
stoatflow.commit-barrier.max-interval-msbarrierMaxIntervalMs5000Ceiling on barrier-to-barrier time.
stoatflow.commit-barrier.interval-factorbarrierIntervalFactor1.5Multiplier applied to recent commit duration when spacing barriers, clamped to [min, max] (>= 1.0). Rarely changed.
stoatflow.commit-barrier.timeout-msbarrierTimeoutMs60000Bounds 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-recordsbarrierMaxEpochRecordsunset (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
Tighter latency vs. lower overhead. Lowering 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.
A stalled commit is not silent: if a barrier can't complete within 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 keyStreamsConfig fieldDefault
stoatflow.processing-guaranteeprocessingGuaranteeEXACTLY_ONCE
ValueBehaviour
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_ONCENon-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 keyStreamsConfig fieldDefaultNotes
stoatflow.state.dirstateDir${java.io.tmpdir}/stoatflowPut 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-bytesstateStoreUncommittedMaxBytes268435456 (256 MB)Global cap on buffered uncommitted state; exceeding it triggers an early barrier.
stoatflow.state.segment-checkpoint-recordssegmentCheckpointRecords100000Periodic 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-mssegmentCheckpointMs30000 (30 s)The same checkpoint cadence by wall-clock time; whichever-first with segment-checkpoint-records.
stoatflow.rocks-db.presetrocksDbConfigDEFAULT (256 MB)DEFAULT / LOW_MEMORY (64 MB) / HIGH_PERFORMANCE (1 GB) total RocksDB memory.
stoatflow.rocks-db.backendrocksDbBackendTypeAUTOAUTO 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 keyStreamsConfig fieldDefaultNotes
stoatflow.changelog.enabledchangelogEnabledtrueWhen on, state mutations are written to changelog topics inside the same transaction.
stoatflow.changelog.create-topics-if-not-existcreateChangelogTopicsIfNotExisttrueAuto-creates compacted changelog topics on startup.
stoatflow.changelog.replication-factorchangelogReplicationFactor-1 (broker default)Set explicitly (e.g. 3) for production durability.
stoatflow.changelog.num-partitionschangelogNumPartitions1One partition is usually sufficient for single-instance.
stoatflow.state.restoration-enabledstateRestorationEnabledtrueRestore state from changelog on startup when local state is missing.
stoatflow:
  changelog:
    enabled: true
    replication-factor: 3
For production, set 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 keyStreamsConfig fieldDefaultNotes
stoatflow.watermark.max-out-of-orderness-msmaxOutOfOrderness10000 (10 s)How far out of order events may arrive before being treated as late.
stoatflow.watermark.idleness-timeout-mswatermarkIdlenessTimeout300000 (5 min)Partitions idle this long are excluded from the global watermark. 0 disables.
stoatflow.watermark.auto-interval-msautoWatermarkIntervalMs200Periodic watermark emission interval.
stoatflow.processing.timestamp-coordination-modetimestampCoordinationModeAUTOAUTO (derive from topology), ENABLED (force on), DISABLED (force off, benchmarking).
stoatflow.processing.max-task-idle-msmaxTaskIdleMs0Cross-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