Streaming providers
Fleans queues custom tasks (ExecuteCustomTaskEvent) for gRPC workers through
Orleans Streams. Script tasks and conditions do not use a stream: a workflow instance evaluates
gateway and other conditions itself, and sends each script task straight to the script executor,
which reports the result back — so they never wait for a queue poll. The stream provider is pluggable at startup:
the default is Redis, and in-memory, Kafka and Azure Queue Storage providers ship alongside it.
The read model rides on the same provider: after an append that changes what lists show, an
instance publishes InstanceAdvanced(instanceId, version) on the events.InstanceAdvanced
namespace, one stream per instance, and a projector grain on the worker silos builds the read model
from the instance’s event log. A provider that loses messages (in-memory across a restart, Redis
without persistence) can leave a list row behind until that instance next activates.
Signal broadcasts ride on it too: a broadcast queues its deliveries on the events.SignalDelivery
namespace, one stream per signal bucket, and the instances receive the signal from there at the
pace set by Fleans:Signals:DeliveryConcurrency.
Provider switch
Section titled “Provider switch”The provider name is read once at silo startup from configuration:
| Configuration key | Values | Default |
|---|---|---|
Fleans:Streaming:Provider | redis, memory, kafka, azurequeue | redis |
Fleans:Streaming:Kafka:Brokers | host:port[,…] | — |
Fleans:Streaming:Kafka:ConsumerGroup | string | fleans |
Fleans:Streaming:Kafka:TopicPrefix | string | fleans- |
Fleans:Streaming:Kafka:QueueCount | integer | 8 |
Fleans:Streaming:Kafka:ReplicationFactor | integer | 3 (1 on a single-broker cluster) |
Fleans:Streaming:AzureQueue:ConnectionString | Azure Storage connection string | — |
Fleans:Streaming:AzureQueue:AccountName | Azure Storage account name | — |
Fleans:Streaming:AzureQueue:QueueNames | JSON array of strings | ["fleans-stream-0"…"fleans-stream-7"] |
Fleans:Streaming:AzureQueue:MessageVisibilityTimeout | TimeSpan string (e.g. 00:01:00) | Azure SDK default (30 s) |
Match is case-insensitive. Any other value throws ArgumentException at startup —
add a new case to FleanStreamingExtensions.AddFleanStreaming if you ship another provider.
For azurequeue, exactly one of ConnectionString or AccountName must be set.
ConnectionString is used for local dev (Azurite). AccountName enables Managed Identity
(DefaultAzureCredential) for production Azure deployments.
Choosing a provider
Section titled “Choosing a provider”memory — single silo, no infrastructure
Section titled “memory — single silo, no infrastructure”Backed by AddMemoryStreams. Zero infrastructure. Events are lost on silo crash: in-flight
stream messages live only in the silo’s memory.
Right for: local development, all default tests, and single-silo deployments where you accept the
event-loss profile of a silo crash — the single-PostgreSQL layout.
The engine’s default is redis.
kafka — opt-in cross-silo durability
Section titled “kafka — opt-in cross-silo durability”Backed by an in-repo Fleans.Streaming.Kafka adapter built on Confluent.Kafka 2.6.1
(librdkafka 2.6.x bundled).
Right for: production rollouts that need event durability beyond a single silo, CI environments that want broker parity with production, and topologies where a non-Orleans consumer may join the topics later (note: the codec is currently the Orleans codec — see limitations below).
azurequeue — opt-in cross-silo durability (Azure-native)
Section titled “azurequeue — opt-in cross-silo durability (Azure-native)”Backed by Microsoft.Orleans.Streaming.AzureStorage — the first-party Microsoft Orleans
adapter for Azure Queue Storage. This is a thin wrapper, not a custom adapter, so behaviour,
retry semantics, and dead-letter handling are governed by the Microsoft package.
Right for: Azure-native deployments that need cross-silo event durability without running Kafka.
Uses Azure Queue Storage (cheaper than Service Bus at scale; Orleans StreamSequenceToken
provides per-queue ordering). In dev mode, Aspire auto-provisions Azurite so no Azure
subscription is needed.
For auth options: set ConnectionString for Azurite or an explicit connection string; set
AccountName to use DefaultAzureCredential (Managed Identity, recommended for Container Apps).
Kafka security (SASL / TLS)
Section titled “Kafka security (SASL / TLS)”Plaintext is the default. To connect to a broker that requires authentication or encryption, set
these keys under Fleans:Streaming:Kafka on every Fleans workload (as environment variables:
Fleans__Streaming__Kafka__<Key>). An invalid combination — a SASL protocol without a
mechanism, a certificate without its key, TLS files with a plaintext protocol — stops the silo at
startup with an error naming the setting.
| Key | Values | Notes |
|---|---|---|
SecurityProtocol | Plaintext (default), Ssl, SaslPlaintext, SaslSsl | Managed services almost always use SaslSsl. |
SaslMechanism | Plain, ScramSha256, ScramSha512 | Required with SaslPlaintext / SaslSsl. |
SaslUsername / SaslPassword | Required with Plain and the Scram* mechanisms. | |
SslCaLocation | PEM file path | CA that signed the broker certificate. Omit to trust the OS store. |
SslCertificateLocation / SslKeyLocation | PEM file paths | Client certificate and key for mutual TLS; set both. |
SslKeyPassword | Only for a passphrase-protected key. |
Use absolute paths for the Ssl*Location files — they resolve against the silo’s working directory.
OAUTHBEARER and Amazon MSK IAM authentication need a token callback in code, so the released images
can’t use them; connect to MSK through its SASL/SCRAM or TLS listener instead.
Add the keys to every fleans-* service in your docker-compose.override.yaml, for example SASL/SCRAM over TLS:
services: fleans-core: environment: &kafka Fleans__Streaming__Provider: Kafka Fleans__Streaming__Kafka__Brokers: "broker.example.com:9093" Fleans__Streaming__Kafka__SecurityProtocol: SaslSsl Fleans__Streaming__Kafka__SaslMechanism: ScramSha512 Fleans__Streaming__Kafka__SaslUsername: fleans Fleans__Streaming__Kafka__SaslPassword: ${KAFKA_PASSWORD} fleans-management: environment: *kafka fleans-mcp: environment: *kafka fleans-worker: environment: *kafkaThe chart sets these keys from streaming.kafka.security (chart 0.11.0 and later; on 0.10.x set the Fleans__Streaming__Kafka__* variables through extraEnv). Secrets come from existing Kubernetes
Secrets: the SASL password by key, and the TLS files as a Secret with ca.crt (plus tls.crt
and tls.key for mutual TLS) — the layout cert-manager writes. The chart mounts it at
/etc/fleans/kafka on every Fleans pod and points the Ssl*Location keys there.
streaming: provider: Kafka kafka: brokers: "broker.example.com:9093" security: protocol: SaslSsl saslMechanism: ScramSha512 saslUsername: fleans saslPasswordSecret: kafka-credentials # key "password" (saslPasswordSecretKey) tlsSecret: kafka-tls # optional: ca.crt for a private CA clientCertificate: false # true: also present tls.crt / tls.key (mTLS)An unknown protocol, or tlsSecret without Ssl / SaslSsl, fails helm template. For a
passphrase-protected key, add Fleans__Streaming__Kafka__SslKeyPassword through extraEnv.
Production-readiness gaps
Section titled “Production-readiness gaps”What the Kafka adapter still leaves to you. Durability is production-typical by default: topics are
created with ReplicationFactor=3 (1 on a single-broker cluster, with a startup warning on a
partially provisioned one), and the producer is idempotent with acks=all.
| Gap | Why it hurts | Workaround |
|---|---|---|
| At-least-once delivery (offset commit after handler success) | Silo crash between handler success and offset commit → event replays | Safe by design — every consumer routes to a WorkflowInstance method guarded by HasActiveEntry. Do not remove that guard. See Delivery contract |
| Dead-letter topic off by default | A record that keeps failing is dropped after DeliveryRetryWindow with only an error log | Set EnableDeadLetterQueue=true — see Poison records |
| One partition per topic | Records are ordered by offset, which Kafka orders only within a partition, so a topic must not be split | Raise QueueCount for parallelism — each queue is its own topic. Startup fails on an existing topic with more than one partition; never kafka-topics --alter --partitions. See Tuning throughput |
| No schema registry integration | Codec is the Orleans codec; non-Orleans consumers cannot decode the stream | Treat Kafka topics as silo-internal |
| Consumer lag is a metric, not a health check | A lagging consumer doesn’t turn the silo unhealthy | Alert on the fleans.kafka.consumer.lag gauge — see Observability |
Manual test plan #35 exercises the happy path against a single-broker Aspire-provisioned fleans-kafka resource — it validates that the at-least-once contract holds across a silo restart, but it does not test any production failure mode.
Production-readiness gaps — Azure Queue Storage
Section titled “Production-readiness gaps — Azure Queue Storage”The Azure Queue provider is backed by the Microsoft-maintained Microsoft.Orleans.Streaming.AzureStorage package, which covers several production concerns automatically.
| Concern | Status |
|---|---|
Managed Identity (AccountName path) | ✅ Covered — uses DefaultAzureCredential |
| Message retry / poison-message handling | ✅ Covered — MS provider retries up to configurable limit; exhausted messages move to *-poison queue automatically |
| OTLP monitoring | ✅ Covered — Orleans Streams telemetry emitted via Orleans.Telemetry; no extra wiring needed |
| Managed Identity token rotation | ✅ DefaultAzureCredential handles refresh transparently |
| Cross-queue message ordering | ⚠️ Per-queue FIFO only — ordering across the 8 default fleans-stream-* queues is not guaranteed (same trade-off as Kafka partitions) |
Manual test plan #44 exercises the happy path with Azurite.
Delivery contract — at-least-once
Section titled “Delivery contract — at-least-once”The Kafka adapter checkpoints offsets after the consumer-side handler completes. On a silo crash between handler success and offset commit, the next consumer instance resumes from the last committed offset, which means the in-flight event will replay.
HasActiveEntry guard turns the duplicate into a logged no-op. Open interactive diagram ↗The replay is safe because every Fleans consumer eventually routes to a method on
WorkflowInstance (CompleteActivity, FailActivity, …) that opens with a stale-callback
guard:
if (!State.HasActiveEntry(activityInstanceId)){ LogStaleCallbackIgnored(...); return;}Once the activity instance has completed once, its entry leaves HasActiveEntry, so a second
delivery is a logged no-op. Do not remove that guard without re-reading this page —
it is what makes the at-least-once contract safe for the workflow engine.
Replays also happen without a crash. Each Kafka topic is read by the silo the Orleans queue balancer gives it to; the silos do not join the Kafka consumer group. When a queue moves to another silo (a silo starts or stops), the old and new owners can both read it for a moment, so a few events are delivered twice. The guard handles these the same way. A custom task handler with external side effects should be idempotent for the same reason.
Poison records and the dead-letter topic
Section titled “Poison records and the dead-letter topic”When a subscriber keeps throwing on a record, Orleans retries delivery for up to
DeliveryRetryWindow (unset by default: MaxEventDeliveryTime, 6 minutes, applies), then gives up and moves on to the next record.
By default that record is dropped with only an error log.
Set Fleans__Streaming__Kafka__EnableDeadLetterQueue=true to keep it instead. Each such
record is then published, unchanged, to a dead-letter topic named after its source topic
(fleans-0 → fleans-0-dlq). The engine creates the dead-letter topics at startup alongside
the regular ones.
| Setting | Default | What it does |
|---|---|---|
Fleans__Streaming__Kafka__EnableDeadLetterQueue | false | Route records whose subscriber keeps failing, and records a silo cannot deserialize, to the dead-letter topic instead of dropping them. |
Fleans__Streaming__Kafka__DeadLetterTopicSuffix | -dlq | Suffix appended to each source topic. The silo refuses to start if the suffix would make a dead-letter topic name equal a regular topic (for example 0: fleans-1 + 0 = fleans-10). |
Fleans__Streaming__Kafka__DeliveryRetryWindow | unset (MaxEventDeliveryTime, 00:06:00) | How long to keep retrying a failing record before giving up (hh:mm:ss), overriding MaxEventDeliveryTime for Kafka. The stream stays blocked for that subscriber while it retries. Keep it longer than your slowest custom task, or that task’s event is skipped and the task never runs. |
Each dead-letter record keeps the original record’s headers and adds these:
| Header | Value |
|---|---|
x-fleans-original-topic / -partition / -offset | Where the record came from. |
x-fleans-failure-reason | delivery-failed (a subscriber kept throwing) or deserialization-failed (the silo reading the queue could not decode the record). |
x-fleans-failure-time-utc | ISO-8601 timestamp. |
x-fleans-stream-id | Orleans stream ID (delivery-failed only). |
x-fleans-subscription-id | The subscription whose handler kept failing (delivery-failed only). |
x-fleans-exception | Exception type and message (deserialization-failed only). |
A record whose bytes cannot be deserialized is not retried: it is skipped right away and logged at
Error — EventId 11113 when it goes to the dead-letter topic, 11107 when the DLQ is off and it
is dropped. Two causes are known: something other than the engine wrote to the topic, or a silo
reading the queue runs an older Fleans version than the silo that wrote the record. Every silo that registers
the stream provider reads a share of the queues — keep them on one Fleans version. Custom-task workers
read no queues.
Delivery to the dead-letter topic is at-least-once: a record that fails for several subscribers
is published once per subscriber, each copy with its own x-fleans-subscription-id. Deduplicate
on the x-fleans-original-* headers. The whole Kafka record is dead-lettered, so replaying it
also redelivers any events in it that had already succeeded — safe, because of the at-least-once
contract above.
Inspect the topic with any Kafka client, for example:
kafka-console-consumer.sh --bootstrap-server <broker> --topic fleans-0-dlq \ --from-beginning --property print.headers=trueTo retry a record once the cause is fixed, produce its value back to x-fleans-original-topic.
Redelivery is safe: the engine ignores events for activities that have already completed (see
the at-least-once contract above). Every dead-lettered record is logged at Warning with
EventId 12000.
The dead-letter topic is best-effort, not a guarantee. If publishing to it is not confirmed within
30 seconds (EventId 12001, Error), the record may still arrive later, or may be lost. If the
broker no longer holds the original record (EventIds 12004–12006), the record is lost: the
engine has already moved past it. Connection, authentication or TLS problems while re-reading
the record are logged as EventId 12007. Alert on EventId 12001. A record is also lost if
the silo crashes while Orleans is still retrying it
(#813).
Topic provisioning
Section titled “Topic provisioning”KafkaQueueAdapterFactory.CreateAdapter() runs a one-shot topic-ensure step on first activation:
- Build the expected topic list (
{TopicPrefix.TrimEnd('-')}-{0..QueueCount-1}). AdminClient.GetMetadatato discover what already exists on the broker.- Fail startup if an expected topic already exists with more than one partition — recreate it with one (Kafka cannot shrink a topic).
AdminClient.CreateTopicsAsyncfor missing topics with one partition and the configuredReplicationFactor.TopicAlreadyExistsandNoErrorare both treated as success (a peer silo may have just created the topic).
The broker-side allow.auto.create.topics flag is not required — it is documented here as
a fallback for self-managed clusters that prefer it, but managed clusters (Confluent Cloud, MSK)
disable it by default and the client-side path is what we ship.
Switching the streaming provider
Section titled “Switching the streaming provider”Set Fleans__Streaming__Provider on every fleans-* workload via your deployment tool. The engine accepts four values: Redis (default), Memory, Kafka, AzureQueue. Both the Docker Compose bundle and the Helm chart ship with sensible defaults — you only need an explicit switch when moving off the default.
The bundle ships with Redis as the default streaming provider. You only need an override file to switch away from Redis — for the production-default Redis behaviour, no edits are required.
Drop a docker-compose.override.yaml next to the bundle’s docker-compose.yaml with the same env var on every fleans-* service:
services: fleans-core: environment: Fleans__Streaming__Provider: Memory # or Kafka, AzureQueue fleans-management: environment: Fleans__Streaming__Provider: Memory fleans-mcp: environment: Fleans__Streaming__Provider: Memory fleans-worker: environment: Fleans__Streaming__Provider: MemoryFor Kafka, add Fleans__Streaming__Kafka__Brokers: "kafka:9092" (replace with your broker host:port). For Azure Queue, add Fleans__Streaming__AzureQueue__ConnectionString: "<your-connection-string>". Then docker compose up -d from the bundle directory — Compose merges the override automatically.
This streaming override is a pure service-level env addition (unlike the worker override in Writing custom tasks, which depends on the fleans_default network being created by an engine up first). You can docker compose down && docker compose up -d from the bundle directory at any time to apply the change — engine-first ordering is not required.
Redis is the chart default (the chart-bundled Redis); nothing to set unless you’re switching away from it.
# Kafkahelm install fleans nightbaker/fleans \ --set streaming.provider=Kafka \ --set streaming.kafka.brokers="kafka.kafka.svc:9092"
# Azure Queue Storagehelm install fleans nightbaker/fleans \ --set streaming.provider=AzureQueue \ --set streaming.azureQueue.connectionString='<your-connection-string>'
# Memory — single silo onlyhelm install fleans nightbaker/fleans --set streaming.provider=MemoryAn unknown streaming.provider fails helm template with the list of valid values.
(For the source-tree dev workflow — only relevant if you cloned the engine repo — see the engine CLAUDE.md’s Things to Know section for the AppHost-side FLEANS_STREAMING_PROVIDER knob.)
Provider notes
Section titled “Provider notes”Redis (default): Reuses the same Redis instance the engine already runs for clustering, so stream events cost zero extra infrastructure. Uses the third-party Universley.OrleansContrib.StreamsProvider.Redis package (MIT, Orleans 10.x-compatible), pinned in Fleans.ServiceDefaults.csproj. Production requirement: enable Redis persistence (AOF or RDB) — without it, a Redis restart loses in-flight messages. The chart’s bundled fleans-redis enables AOF by default; see Self-host with Helm for the Redis subchart values.
Memory: No external infrastructure. Drops in-flight events on silo restart — single-silo debug-only. Suitable for local dev and CI smoke tests; never for production.
Kafka: Supply your own Kafka cluster — the chart’s streaming.kafka.brokers value takes a Kafka bootstrap-server string; for SASL or TLS brokers see § Kafka security. For an in-cluster Kafka, the Bitnami Kafka Helm chart is the conventional choice. The silo forwards Fleans__Streaming__Provider=Kafka + Fleans__Streaming__Kafka__Brokers to the Kafka producer.
AzureQueue: Supply an Azure Queue Storage connection string — see Microsoft’s Azure Storage connection-string docs for the format. For local dev, run Azurite as a sibling Compose service and point the connection string at it. The silo forwards Fleans__Streaming__Provider=AzureQueue + Fleans__Streaming__AzureQueue__ConnectionString to the Azure Queue producer.
Connection multiplexer aliasing (Redis only)
Section titled “Connection multiplexer aliasing (Redis only)”The third-party Redis stream provider resolves a non-keyed IConnectionMultiplexer from DI. Aspire registers it as a keyed service (AddKeyedRedisClient("orleans-redis")). FleanStreamingExtensions.AddRedisStreams bridges the two with TryAddSingleton<IConnectionMultiplexer>(...) — reusing the same connection pool, no duplicate sockets. If another component needs a different non-keyed multiplexer, register it explicitly before AddFleanStreaming runs and TryAddSingleton will defer.
Subscriptions and event durability
Section titled “Subscriptions and event durability”Fleans subscribes to streams only through implicit subscriptions: each handler grain declares the
stream namespaces it consumes, and Orleans routes events by that declaration. So there is no
subscription state to store, and no PubSubStore grain storage to configure. Every silo in the
cluster, custom-worker hosts included, uses this mode through AddFleanStreaming; a host that
registers its stream provider by hand must call
ConfigureStreamPubSub(StreamPubSubType.ImplicitOnly) too.
What changes when you opt into Kafka is event durability — the bytes flowing between
WorkflowEventsPublisher.Publish and a custom-task handler’s OnNextAsync. With the
in-memory provider, those bytes live in the silo’s process memory. With Kafka, they land in a
broker topic before the handler runs.
Compatibility note
Section titled “Compatibility note”Upgrading from v0.10.0 or earlier with Kafka: stop every silo first. Newer silos read their
topics without joining the Kafka consumer group, and they must not share ConsumerGroup with older
silos that still join it: the broker rejects the new silos’ offset commits, and both versions
read the same events. Scale every silo deployment to zero before upgrading; a rolling update would
run both versions together. Changing ConsumerGroup to avoid the stop is no shortcut: a group with no
committed offsets reads every topic again from the start.
Confluent.Kafka 2.6.1 / librdkafka 2.6.x are the supported versions. The adapter targets
net10.0 (matching every other silo csproj in this repo).
Tuning throughput
Section titled “Tuning throughput”Throughput across the queue-backed providers is governed by two independent classes of knobs. The first is an Orleans-level concept shared by every provider; the second is Kafka-specific and does not have analogues elsewhere.
Orleans parallelism (all queue-backed providers)
Section titled “Orleans parallelism (all queue-backed providers)”Fleans:Streaming:Redis:TotalQueueCount, Fleans:Streaming:Kafka:QueueCount, and the length of Fleans:Streaming:AzureQueue:QueueNames all set the same thing: the number of Orleans pulling-agent grains that activate across the cluster. Stream IDs hash across this many partitions; each partition is consumed by one pulling agent. More partitions = more parallel consumption.
| Provider | Knob | Default | Notes |
|---|---|---|---|
| Redis | Fleans__Streaming__Redis__TotalQueueCount | 8 | Configurable as of v0.2.0 (#567). |
| Kafka | Fleans__Streaming__Kafka__QueueCount | 8 | Was 1 before v0.2.0 (#567) — call out as a behavior change for deployments that left it unset. |
| AzureQueue | Fleans__Streaming__AzureQueue__QueueNames__0..N | 8 entries (fleans-stream-0..7) | No scalar count — tuning above OR below 8 requires populating the explicit list by hand. Mid-deployment length changes rehash Stream IDs across queues. |
Use the same value on every silo. Every silo must run with the same queue count (and, for AzureQueue, the same QueueNames list). Orleans does not check this: silos with different values route the same stream to different queues, and workflows stall with no error in the logs. Set the value explicitly in shared configuration rather than per host.
Sizing heuristic. Start with max(8, 2 × silo_count × cpu_per_silo) for high-volume deployments and measure with the Orleans dashboard (wired at Fleans.Api/Program.cs) before raising further. In a multi-silo deployment, agents distribute across silos via the hash ring — total cluster-wide count = the configured value, per-silo count ≈ ceil(value / silo_count).
Rehash caveat (all providers). Bumping the count rehashes Stream IDs across queues. Consistent with the project’s pre-v1 stance, expect in-flight workflow stalls across the bump window — no formal drain procedure is provided. After the bump, new workflow instances distribute over the new queue count; in-flight instances that straddle the rehash may stall until reactivation picks them up on the new queue assignment.
Tradeoff. Each partition adds a pulling-agent grain per silo plus provider-side resource overhead (a Redis connection, a Kafka consumer, an AzureQueue receiver lease).
Production caveats already documented elsewhere. Redis streaming requires AOF or RDB persistence — see Provider notes above; Kafka requires its own cluster lifecycle ops — see Production-readiness gaps.
Poll period (all queue-backed providers)
Section titled “Poll period (all queue-backed providers)”| Knob | Default | What it does |
|---|---|---|
Fleans__Streaming__PollPeriod | 00:00:00.100 (Orleans default) | How often each pulling agent reads its queue. A stream event waits up to one period before delivery, so it bounds the per-hop latency of custom tasks and read-model updates. A shorter period lowers that wait and polls the backend (Redis, Kafka, Azure Queue) more often. Must be a positive TimeSpan. |
Script tasks and conditions are not affected: they do not travel on a stream.
Delivery window and Redis trimming
Section titled “Delivery window and Redis trimming”| Knob | Default | What it does |
|---|---|---|
Fleans__Streaming__MaxEventDeliveryTime | 00:06:00 | How long a silo keeps retrying one stream event before skipping it for good. Nothing is logged when an event is skipped and the instance stays stuck, so the window must be longer than your slowest custom task. Set the same value on engine silos and plugin hosts. Must be a positive TimeSpan. |
Fleans__Streaming__Redis__MaxStreamLength | 100000 | Maximum entries kept per Redis stream (one stream per queue). Redis trims by length whether or not an entry was delivered, so a backlog longer than this loses events. Size it above the largest backlog you expect (peak events per second per queue × the longest consumer outage) with headroom; memory grows with length × TotalQueueCount. |
The trade-off of a long window: a subscriber that keeps failing holds its queue for the whole window before the event is dropped (or, on Kafka with EnableDeadLetterQueue, dead-lettered).
Fleans also sets how long a silo keeps a stream it has stopped seeing events on to twice the window (12 minutes by default). Every custom task has its own stream, so this bounds the per-stream memory a silo holds; a larger window raises it too.
Kafka-specific Kafka-side tuning
Section titled “Kafka-specific Kafka-side tuning”Every topic has exactly one partition: Fleans orders records by offset, which Kafka orders only within a partition. Scale with QueueCount — each queue is its own topic, so it sets both the Orleans consumer count and the broker-side partition count. If a partition is added to a live topic, running silos still read it (so no record is lost) but log a warning (EventId 11118), and the next startup fails until the topic is recreated.
| Knob | Default | What it does |
|---|---|---|
Fleans__Streaming__Kafka__ReplicationFactor | 3 | Per-topic replication factor at topic creation. Capped at the live broker count: a single-broker cluster gets 1, and a cluster with fewer brokers than the setting logs a warning at startup. |
See also
Section titled “See also”- Observability — health checks, metrics, logging, tracing, dashboards, alerting
- Deployment — how to wire
Fleans:Streaming:Provider/Fleans:Streaming:Kafka:Brokersinto Docker Compose, Kubernetes, and bare-VM deployments. - Self-Hosting on Kubernetes — Kafka opt-in on the Helm chart.