State Stores in Kafka Streams
How Kafka Streams applications remember things between events, how that memory is stored locally, protected by Kafka itself, recovered after failures, and queried from the outside.
Imagine a cashier at a busy shop who has to tell every customer how much they have spent so far this month. If the cashier had no notebook, each new purchase would arrive as a stranger and the cashier could only say “this item costs five.” With a notebook, the cashier writes down a running total for every customer and can answer instantly. A state store is that notebook for a Kafka Streams application, and Kafka keeps a photocopy of it in a safe place so it is never lost if the notebook gets wet.
This tutorial explains state stores from the ground up. We start with why applications need memory at all, then look at how the notebook is organised, where it lives, how it survives crashes, how it moves when the application scales, and how other programs can read it. Every new word is explained the first time it appears, with an everyday comparison.
1Why Applications Need to Remember
Before we meet the state store, we need to understand the problem it solves: events arrive one at a time, but useful answers often depend on many events.
A quick Kafka refresher
Apache Kafka stores streams of events in topics. An event is a small record of something that happened, such as a payment, a page view or a temperature reading. Programs that write events are producers, and programs that read them are consumers. Kafka Streams is a library that lets you write programs that read events from topics, process them, and write new events to other topics. It runs inside your own application, not as a separate server.
Stateless versus stateful processing
Some processing needs no memory. Converting a temperature from Celsius to Fahrenheit, dropping events that are missing a field, or renaming a column looks at one event at a time and forgets it immediately. This is called stateless processing. Each event can be handled alone, so the program needs nothing beyond the event itself.
Other processing needs memory. Counting how many orders each customer placed, adding up the money spent per day, joining a stream of clicks with a table of user profiles, or detecting three failed logins within five minutes all require the program to remember what came before. This is called stateful processing, and the remembered information is called state.
Stateless processing is like a toll booth attendant who only checks whether each car has a valid ticket, then forgets it. Stateful processing is like a librarian who tracks how many books each member has borrowed and reminds them when they reach the limit. The librarian needs a record book. The attendant does not.
flowchart LR
subgraph SL["Stateless: each event handled alone"]
A1["Event in"] --> A2["Transform or filter"] --> A3["Event out"]
end
subgraph SF["Stateful: needs memory of earlier events"]
B1["Event in"] --> B2["Read remembered value"]
B2 --> B3["Update value"]
B3 --> B4["Event out"]
B3 --> B5[("State")]
B5 --> B2
end
Whenever your streaming logic uses words like “count”, “total”, “join”, “window”, “deduplicate” or “latest value per key”, your application is stateful and will use one or more state stores.
2What Is a State Store?
A state store is the place where a Kafka Streams application keeps its remembered information.
A state store is an embedded, local storage area that a Kafka Streams application uses to hold the state needed by stateful operations. It behaves like a small key-value database sitting right next to your processing code. Your application can quickly read a value by its key, write a new value, delete it, or scan a range of keys. Because it lives on the same machine as the code, reads and writes are fast and do not need a network trip to a separate database.
Three properties that define it
Local
Each application instance keeps its own store on its own disk or memory, close to the code that uses it.
Partitioned
The state is split by key so each instance only holds the share that belongs to the data it processes.
Fault tolerant
Every change is also written to a Kafka topic, so a lost store can be rebuilt from Kafka.
Embedded
There is no separate database cluster to install, secure and operate. The store is part of the application.
Think of each cashier in a supermarket having a personal notebook at the counter. Nobody needs to phone a head office to check a customer’s total because the cashier has the page right there. The head office also keeps a duplicate ledger, updated after every entry, in case a notebook is lost.
Why not just use an external database?
You could store state in an outside database, and some applications do. But every event would then need a network call to read and another to write, which adds delay and creates a dependency on a separate system that must be scaled and kept available. State stores avoid this by keeping data local and using Kafka, which you already run, as the durable backup. The trade-off is that the state is not easily shared with arbitrary other programs, which is the reason a feature called interactive queries exists, covered later.
3Where State Stores Live: Tasks, Partitions and Instances
To understand where the data actually sits, we need three words: task, thread and instance.
Tasks: the unit of work
When a Kafka Streams application starts, it reads the topology, which is the graph of processing steps you defined, and creates tasks. A task is the smallest unit of work and is tied to one partition number of the input topics. If your input topic has six partitions, the application creates six tasks. Each task processes only the events from its own partition, in order.
One store shard per task
Each task that has a stateful step gets its own private piece of the state store. So a store is not one big database. It is a collection of small shards, one per task, and each shard holds only the keys that appear in that task’s partition. Because the same key always goes to the same partition, the same key always lands in the same shard, which keeps the logic consistent.
Threads and instances
Tasks are run by stream threads, and one application can run several threads. You can also run several copies of the same application, each called an instance, perhaps on different machines. Kafka spreads the tasks across all threads and instances. Every task, along with its store shard, runs in exactly one place at a time.
flowchart TB
subgraph Topic["Input topic with 4 partitions"]
P0["P0"]
P1["P1"]
P2["P2"]
P3["P3"]
end
subgraph I1["Instance 1"]
T0["Task 0"] --- S0[("Store shard 0")]
T1["Task 1"] --- S1[("Store shard 1")]
end
subgraph I2["Instance 2"]
T2["Task 2"] --- S2[("Store shard 2")]
T3["Task 3"] --- S3[("Store shard 3")]
end
P0 --> T0
P1 --> T1
P2 --> T2
P3 --> T3
A large library splits its members alphabetically among four help desks. Each desk keeps a card index only for its own letters. A member with a surname beginning with “M” always goes to the same desk, so their card is always found in one place, and no desk needs the whole index.
Beginners often picture one shared database for the whole application. In reality there are many small stores, and the number of tasks, which equals the number of input partitions, decides how the state is split and how many parallel workers can help.
4Types of State Stores
Different questions need different shapes of storage, so Kafka Streams offers several kinds of stores.
Key-value store
The simplest shape. Each key has one current value. Used for counts, running totals and latest-value tables.
Window store
Stores values per key within fixed time windows, such as orders per customer per hour.
Session store
Stores values per key for bursts of activity separated by quiet gaps, such as one browsing visit.
Timestamped store
Keeps the time of each value along with the value itself, so time-aware logic can judge freshness.
Versioned store
Keeps several past versions of a value per key, allowing lookups such as “what was the value at that moment?”
Global store
A full copy of an entire topic on every instance, used for small reference data that everyone needs.
Key-value stores in depth
A key-value store is like a dictionary. You look up a key and get its value. When you count clicks per user, the key is the user ID and the value is the count. Each new click reads the old count, adds one and writes the new count. This store shape also supports listing all keys in a range, which is useful for reports.
Window stores in depth
A window is a slice of time. A window store records a separate value for each key and each time slice. Counting logins per user per five minutes creates many small entries, one per user per five-minute window. Old windows are removed after a retention period so the store does not grow forever.
Session stores in depth
A session is a period of activity followed by a gap of silence. Unlike fixed windows, sessions have no fixed length. They open when an event arrives and close after the quiet gap passes. If two sessions get joined by a late event, the store merges them into one.
A key-value store is a phone book with one number per name. A window store is a diary with one page per day. A session store is a diary of conversations, where each entry lasts as long as people keep talking and ends when they go quiet.
| Store Type | Grouped By | Typical Use | Cleaned Up By |
|---|---|---|---|
| Key-value | Key | Counts, latest value, lookups | Explicit delete or compaction |
| Window | Key and fixed time slice | Hourly totals, rate limits | Retention period |
| Session | Key and activity burst | Visits, user journeys | Retention period |
| Versioned | Key and history of versions | Time-aware joins | History retention |
| Global | Whole topic copied everywhere | Small reference data | Follows the source topic |
5Persistent Stores versus In-Memory Stores
A store can keep its data on disk or purely in memory, and the choice affects speed, size and recovery.
The persistent store and RocksDB
By default, Kafka Streams uses a persistent store built on RocksDB, an embedded database engine designed for fast writes on modern disks. RocksDB keeps recent changes in memory and organises older data in sorted files on disk. It lets a store hold far more data than the machine’s memory could fit, because only the hot parts need to stay in memory.
Because the data is on disk, a persistent store survives an application restart on the same machine. When the application comes back, it can reuse the files that are already there and only fetch the few changes it missed, instead of rebuilding everything from Kafka.
The in-memory store
An in-memory store keeps everything in the application’s memory. It is very fast and has no disk files to manage, but its size is limited by memory, and everything is lost when the process stops. On restart, the whole store must be rebuilt by replaying its backup topic from Kafka. This can take a long time for large stores.
Persistent (RocksDB) Advantages
- Can hold much more data than available memory
- Survives restarts on the same machine
- Faster recovery because existing files are reused
- Predictable memory use when tuned
Persistent (RocksDB) Trade-offs
- Needs local disk and disk speed matters
- Uses memory outside the usual application memory area
- Needs tuning for large or busy stores
In-Memory Advantages
- Very fast reads and writes
- No disk files or local storage planning
- Simple for small, hot data
In-Memory Trade-offs
- Limited by available memory
- Lost on every restart, so rebuild time is long
- Large stores can cause memory pressure
A persistent store is like keeping records in a filing cabinet with a small desk tray for what you are using right now. An in-memory store is like keeping every record spread on your desk. The desk is quicker to reach, but it holds less and, if the office is cleared overnight, everything must be brought back again.
Choose persistent stores for anything large or anything where fast restart matters. Choose in-memory stores only for small data where a rebuild after restart is quick and acceptable.
6Changelog Topics: How Kafka Protects the State
A local store alone would be lost with the machine. The changelog topic is what makes state stores fault tolerant.
What a changelog topic is
For every store shard, Kafka Streams automatically creates and writes to a special Kafka topic called a changelog topic. Each time a value in the store changes, the new value is also sent to this topic. The topic therefore holds a complete record of the store’s history, and replaying it reproduces the store exactly.
Compaction keeps it small
Changelog topics for key-value stores use log compaction. Compaction keeps only the latest value for each key and discards older values in the background. That means the topic does not grow with every update. It grows only with the number of distinct keys. For window and session stores, the changelog also has a retention limit so old windows eventually disappear from the topic as well.
flowchart LR
IN["Input topic"] --> T["Stream task"]
T --> ST[("Local state store")]
T --> CL["Changelog topic in Kafka"]
T --> OUT["Output topic"]
CL -. "rebuild after failure" .-> ST
A shopkeeper writes each sale in the shop’s notebook and, at the same time, phones the head office to log it in the master ledger. If the notebook burns in a fire, the head office can read the ledger back and the shopkeeper writes a fresh, identical notebook. Compaction is like the head office keeping only the latest balance per customer instead of every single entry.
Naming and ownership
Changelog topic names are built from the application ID, the store name and a suffix. This is why choosing explicit and stable names for stores is important. If a store gets an automatically generated name and the topology later changes, the generated name may shift and the application will look for a changelog that no longer matches its state.
Turning off logging for a store to save space. Without the changelog, a lost or corrupted store cannot be rebuilt, and the data is gone. Only do this for stores whose content can be recreated some other way or genuinely does not matter.
7Internal Working: The Life of One Event
Follow a single event from arrival to output to see exactly how a store is used.
The event arrives at its task
The stream thread polls the input topic and hands each record to the task that owns that partition.
The operator reads the store
For an aggregation, the operator looks up the current value for the record’s key in the local store shard.
The new value is computed and written
The operator combines the old value with the new event, for example adding one to a count, and writes the result back into the store.
The change is logged to the changelog
The updated key and value are also sent to the changelog topic so Kafka holds a durable copy.
Results flow downstream
The updated result is emitted to the next step or to an output topic, either immediately or after a short caching delay.
Progress is committed
At regular commit points, the application flushes pending writes and records how far it has read, so a restart continues from the right spot.
The record cache
Between the operator and the store sits an in-memory cache. If the same key is updated many times in quick succession, the cache holds only the newest value and passes it on when the cache is flushed. This reduces the number of writes to the store and the number of updates sent downstream. The trade-off is that downstream consumers see fewer, slightly delayed updates. The cache is flushed at commit time or when it becomes full.
Instead of walking to the filing cabinet after every single change to a customer’s file, a clerk keeps a sticky note on the desk with the newest total and files it once every few minutes. If the customer buys ten things in a minute, only the final total is filed.
The commit interval
The commit interval controls how often progress is saved. A shorter interval means less work is repeated after a crash but more overhead. A longer interval gives better batching and fewer downstream updates but more repeated work after failures. Under exactly-once processing, the default interval is shorter, because commits also finalise transactions.
If you see fewer output updates than input events for an aggregation, that is usually the cache at work, not lost data. The final values are the same. Only the intermediate updates were merged.
8Failure and Recovery: Restoring State
The true test of a state store is what happens when the machine holding it disappears.
The restore process
When a task starts on a machine and its store shard is missing or out of date, Kafka Streams restores it before processing new events. A special restore consumer reads the changelog topic from the start, or from the last known position if some files exist, and replays each entry into the local store. Only when the store has caught up does the task begin normal processing. This guarantees the task never works with stale or incomplete state.
sequenceDiagram
participant A as Instance A
participant K as Kafka changelog
participant B as Instance B
A->>K: Writes every change to the changelog
Note over A: Instance A crashes
K-->>B: Task is reassigned to Instance B
B->>K: Restore consumer reads changelog
K-->>B: Replays changes into local store
B->>B: Store caught up, resume processing
Checkpoints speed up restarts
For persistent stores, Kafka Streams writes small checkpoint files that record how far each store has been synchronised with its changelog. After a clean restart on the same machine, the application reads the checkpoint and only replays the changes after that point. Without a valid checkpoint, for instance after an unclean crash under exactly-once, it may discard the local files and restore from scratch to guarantee correctness.
How long does restoration take?
Restore time depends on the size of the store and the speed of the network and disk. A store with a few megabytes restores in seconds. A store with hundreds of gigabytes can take a long time, during which those partitions are not processed and their output is delayed. This is the single biggest operational risk for large stateful applications, and the next chapter shows how standby replicas reduce it.
If a shop moves to a new building, the manager must rewrite the customer notebook from the head office ledger before serving anyone. A short ledger takes minutes. A thick ledger takes days. If the manager has been getting nightly copies of the ledger, most of the work is already done.
Deploying on machines that lose their local disk on every restart, such as containers without persistent volumes, forces a full restore each time. For big stores this can turn a routine rolling update into a long outage for the affected partitions.
9Standby Replicas and Warm-Up
Standby replicas keep spare copies of stores ready so a failover is quick.
What a standby replica is
A standby replica is a copy of a task’s store that is kept up to date on a different instance, without processing any input. It only reads the changelog topic and applies the changes to its own local files. If the instance running the active task fails, Kafka Streams can promote the instance holding the standby to active, and processing resumes almost immediately because the store is already nearly current.
flowchart LR
subgraph I1["Instance 1"]
A["Active task 0"] --- SA[("Active store 0")]
end
subgraph I2["Instance 2"]
SB["Standby task 0"] --- SS[("Standby store 0")]
end
A --> CL["Changelog topic"]
CL --> SB
Advantages of Standbys
- Much faster failover for large stores
- Shorter pauses during rebalances
- Better availability of interactive queries
- Smaller risk during rolling deployments
Disadvantages / Trade-offs
- Extra disk space for each replica
- Extra network traffic reading the changelog
- Extra CPU applying updates to the copy
Warm-up replicas during scaling
When you add a new instance, Kafka Streams does not immediately move active tasks onto it. That would force the new instance to restore first while those partitions stall. Instead, it creates warm-up replicas of the tasks that should move. The new instance builds the store in the background while the old owner keeps processing. When the warm-up copy is close enough to the changelog end, the active task is moved over with only a short pause.
A new shop assistant joins, and instead of being thrown in front of customers on day one, they copy the notebooks in the back room while the experienced cashiers keep serving. Only when the copy is nearly complete does the new assistant take over a counter.
Use at least one standby replica for stateful applications with large stores or strict availability needs. Start with one, measure recovery time, and increase only if the numbers demand it.
10Scaling and Rebalancing with State
Because state is tied to tasks, scaling a stateful application needs more care than scaling a stateless one.
What moves during a rebalance
When instances join or leave, the group of instances runs a rebalance, which is the process of redistributing tasks. In a stateless application, a moved task simply starts reading from its new home. In a stateful application, the task also needs its store. If the new owner has no copy, it must restore from the changelog. If it already holds a standby, the move is quick.
Sticky assignment
Kafka Streams uses assignment logic that tries to keep tasks on the instances that already hold their state. It prefers instances that have an active copy, then those that have a standby, and only then instances with nothing. This reduces unnecessary restores and makes rebalances cheaper.
The ceiling on parallelism
The number of tasks equals the number of partitions in the input topics, so you can never run more useful workers than you have partitions. Adding instances beyond that gives no extra processing power. This is why the partition count of input topics for a stateful application should be planned with growth in mind, as changing it later would also change how keys map to store shards.
Increasing the partition count of an input topic for a stateful application breaks the match between keys and store shards. Existing state was built with the old key-to-partition mapping. The safe path is to create a new topic and a new application version and rebuild the state from scratch.
If a library reorganises its members among more desks by changing the alphabet split, every desk’s card index is suddenly wrong for some members. The cards must be redistributed before anyone can be served correctly.
Repartitioning
Sometimes a stateful step needs data grouped by a different key than the one it arrives with. Kafka Streams then automatically inserts a repartition topic, which is an internal topic used to reshuffle events by the new key. The store downstream is then built from the reshuffled data. Repartition topics add storage and latency, so avoid changing keys more often than needed.
11Interactive Queries: Reading the Store from Outside
A state store is not only for internal use. Applications can expose it so other programs can look up current values directly.
What interactive queries are
Interactive queries let you read the contents of a state store while the streaming application is running. Imagine a dashboard that shows the current total spent by a customer. Instead of copying the result into an external database, the streaming application answers the question straight from its own store. This gives very fresh answers and avoids running and syncing another database.
The challenge of distribution
Because the state is split into shards across instances, any single instance only holds part of the data. A request for a customer may arrive at an instance that does not hold that customer’s shard. Kafka Streams provides metadata that tells which instance owns which key. Your application can then forward the request to the right instance, usually through a small web service the team builds and exposes.
flowchart TD
C["Client asks for customer 42"] --> I1["Instance 1 web layer"]
I1 --> D{"Who owns customer 42?"}
D -- "Local shard" --> L["Read local store and answer"]
D -- "Remote shard" --> I2["Forward request to Instance 2"]
I2 --> R["Read its store and answer"]
A visitor asks the front desk of a large library for one member’s record. The desk clerk checks a map that shows which of the four help desks keeps that letter, then either looks it up in their own index or sends the visitor to the correct desk.
Consistency and availability
During a rebalance or restoration, a store may temporarily be unavailable or slightly behind. Queries can be allowed against standby copies to remain available, at the price of potentially slightly older answers. Applications that use interactive queries should be designed to handle these short periods, for example by retrying or accepting slightly stale data.
Interactive queries suit lookups of current values by key, such as a balance, a count or a status. They are not a replacement for a general-purpose database when you need complex searches across many fields.
12Global State Stores and Reference Data
Some data is small and needed everywhere. A global store copies it to every instance.
How a global store differs
A normal store is partitioned. Each instance holds only its shard. A global store is the opposite: every instance holds a complete copy of the entire topic, regardless of partitions. It is loaded from the source topic before processing starts and is kept updated in the background by a dedicated thread. Because the source topic itself already holds the data, no separate changelog topic is created.
Why global stores are useful
Joining a stream to a small reference table, such as country codes or product catalogue entries, normally requires both sides to have matching partitions and keys. With a global store, any event can look up any key locally, no matter which partition it came from. It also allows lookups by a field other than the key, using a mapping function.
Advantages
- No need to match partition counts for joins
- Lookups by any field through a key mapper
- No repartitioning of the stream side
- Every instance answers locally with no network trip
Disadvantages / Trade-offs
- Whole topic copied to every instance, so it must be small
- Startup waits until the global store is loaded
- Updates are not synchronised with stream time
- Not suited to data that changes extremely often
Every cashier keeps a laminated price list of the whole shop at their counter. Each cashier can check any item without asking anyone, but the list must be small enough to fit on the counter, and every cashier needs a new copy when prices change.
Putting a very large topic into a global store. Every instance must hold and load all of it, which slows startup and consumes disk on every machine.
13Where Stores Show Up: Aggregations, Joins and Windows
You rarely create a store by hand. Most stores appear automatically when you use stateful operations.
Aggregations: counts, sums and averages
Grouping events by key and computing a running result requires remembering the current result per key. Kafka Streams creates a key-value store for each aggregation, and the result can be seen as a continuously updated table.
Stream-table joins
When enriching a stream with table data, such as adding customer details to each order, the table side is kept in a store. Each arriving order looks up the customer in that store and joins the two.
Stream-stream joins
Joining two streams of events that happen close in time, such as an ad impression and a click, requires remembering recent events from each side in window stores until a match arrives or the time limit expires.
Windowed aggregations
Counting or summing per key within time slices uses window stores. These support questions such as “how many logins in the last hour” and control late arrivals with a grace period.
Deduplication
To drop repeated events, the application remembers identifiers it has already seen within a time limit. A window store fits well because old identifiers expire automatically.
Table-table joins and foreign-key joins
Joining two changing tables keeps both sides in stores and updates the result whenever either side changes. Foreign-key joins also use internal topics and stores to route updates to the right place.
Naming stores explicitly
Because these stores are created for you, it is easy to leave them with generated names. Give each stateful operation a clear, stable name. Named stores keep the changelog topic names stable across code changes, make monitoring easier to read, and allow you to query stores by name.
Treat the names of stores like database table names. Once data exists under a name, renaming it is like dropping a table and starting again.
14Custom Stores and the Processor API
For special needs, you can define your own processing steps and attach stores to them directly.
What the Processor API adds
The high-level operations cover many cases, but sometimes you need finer control, such as custom timers, unusual logic or manual updates of the store. The lower-level Processor API lets you write your own processing nodes, attach one or more state stores to them, and read and write those stores in any way you like.
Punctuation and scheduled work
A processor can schedule a periodic action, called a punctuation, based either on the passage of event time or on wall-clock time. A common use is periodically scanning a store to emit summaries or to clean out old entries that are no longer needed, such as sessions that have expired by your own rules.
Plugging in other storage engines
Kafka Streams lets you supply a different implementation of a store, as long as it follows the store interface. Teams occasionally do this to use a specialised engine. Most applications are better served by the built-in stores because they come with changelog handling, restoration and monitoring already integrated.
The high-level operations are like a set of ready-made kitchen appliances that handle common recipes. The Processor API is a full professional kitchen, where you can do anything, but you also have to look after the details yourself.
Custom stores that bypass the changelog or keep hidden state outside the store break fault tolerance. Anything that must survive a failure must live in a store that Kafka Streams knows about and backs up.
15State Stores and Exactly-Once Processing
Correct state is only possible if each event affects the store exactly once, even after failures.
The duplicate problem
Imagine the application updates a count, writes to the changelog and output topic, and then crashes before saving its reading position. On restart it would read the same event again and add to the count a second time. For counts, sums and balances, this is wrong.
How exactly-once helps
With exactly-once processing enabled, Kafka Streams groups the store updates in the changelog, the output events and the input position into one transaction. Either everything in a commit becomes visible together, or nothing does. After a crash, the application resumes from the last committed position, and any uncommitted changelog writes are ignored by readers that only see committed data.
Effect on local files
With exactly-once, the local store may contain changes that were not yet committed when a crash occurred. To avoid this hidden inconsistency, Kafka Streams may discard the local files after an unclean shutdown and rebuild them from the committed changelog. This is why clean shutdowns matter and why restore time affects the recovery of exactly-once applications.
A bank teller processes a deposit by updating the balance, stamping the receipt and marking the slip as done in a single action. If the power fails halfway, all three steps are undone together and redone from the start, so the customer is never credited twice.
| Guarantee | What Can Happen to Store State | Suitable For |
|---|---|---|
| At-least-once | Some events may be applied more than once after a failure | Approximate counts, idempotent logic |
| Exactly-once | Each event affects state once, with extra commit and recovery cost | Balances, billing, inventory |
If a duplicated event would produce a wrong business number, such as money or stock, use exactly-once. If the numbers are approximate or the logic can safely repeat, at-least-once is cheaper and simpler.
16Sizing, Storage and Tuning
Stores use disk, memory and network. Planning these resources prevents nasty surprises.
Estimating store size
Estimate the number of distinct keys, multiply by the average size of key plus value, and add overhead for the storage engine. For window stores, multiply again by the number of windows kept within the retention period. Then multiply by the number of copies, counting the active store and each standby replica, to find total disk needs across the cluster.
Disk
Persistent stores need a local directory with enough space and good speed. Solid-state disks give much better performance for busy stores than slow spinning disks. Keep the state directory on stable storage that survives restarts if you want quick recovery.
Memory
RocksDB uses memory outside the normal application memory area for its write buffers and caches. If unbounded, its total use grows with the number of store shards on the machine, and the process can be killed by the operating system for using too much memory. Set a bounded total memory budget for all stores in the process, and leave room for the operating system’s file cache.
Cache size
The record cache is shared among the threads of an instance. A larger cache merges more updates, reducing downstream traffic and store writes. A smaller cache gives more up-to-date output. Tune it to the balance you need between freshness and efficiency.
Retention of windowed data
Windows, sessions and versions are kept only for the retention you configure. Long retention means bigger stores and bigger changelogs. Set retention to the longest time a late event or query could realistically need, and no longer.
Disk
Size for active stores plus standby copies, with generous free space for compaction.
Memory
Bound the storage engine memory across all shards and keep room for the file cache.
Network
Restores and standbys read the changelog, so allow bandwidth for catch-up after failures.
Kafka storage
Changelog and repartition topics consume broker disk and partitions too.
Planning for a state store is like planning a warehouse. You need to know how many boxes you will store, how long they stay, how many copies of the inventory list exist, and how much space forklifts need to move things around.
Sizing memory only for the application heap and forgetting that stores use additional memory outside it. The process then grows until it is killed, often at the busiest moment.
17Security and Governance
State stores hold real business data, so they need the same care as any database.
Protect the local files
Persistent stores keep data in files on the machine. Restrict access to the state directory to the application’s own account, and use disk encryption where the data is sensitive.
Secure the internal topics
Changelog and repartition topics contain the same data as the store. Apply access control rules so that only the application can read and write them, and remember to include them when auditing who can see sensitive data.
Protect interactive query endpoints
If you expose store contents through a web service, require authentication and authorisation, encrypt traffic, and limit which keys and fields a caller may read.
Handle deletion and privacy rules
If regulations require erasing a person’s data, remember it may exist in the store, the changelog and downstream topics. Plan how deletions, often sent as empty values for a key, will flow through all these places.
Limit who can reset state
Tools that reset an application also erase its internal topics and local state. Restrict them, since a mistaken reset means a long rebuild or permanent loss.
Add the state stores of every application to your data catalogue, listing what personal or sensitive data each contains, who owns it, and how long it is kept.
18Monitoring State Stores
Healthy state stores show themselves through a few key signals.
What to watch
Track the time each application spends restoring state, the lag of restore consumers behind their changelog topics, and the lag of standby copies. Watch store read and write latencies and rates, the size of stores on disk, and the memory used by the storage engine. Also monitor stream thread state, rebalance frequency and duration, and consumer lag on input topics.
| Signal | What It Suggests | Typical Action |
|---|---|---|
| Long restore times | Store is large or local disk was lost | Add standbys, use persistent volumes, review retention |
| Growing store size | Missing cleanup or too many keys | Check retention, deletion logic and key design |
| High store read latency | Disk or cache pressure | Faster disks, memory tuning, cache review |
| Frequent rebalances | Instances unstable or timeouts too tight | Review health, timeouts and deployment method |
| Standby lag rising | Replica cannot keep up | Check network, CPU and disk on the standby host |
| Process memory climbing | Storage engine memory unbounded | Set a bounded memory budget |
Monitoring a store is like watching the notebook pile in a shop. If the notebooks keep getting thicker, if rewriting a lost one takes too long, or if the clerk is slowing down because the desk is cluttered, you notice early and act before customers are affected.
Alerts worth having
Set alerts for restores that run longer than your tolerated recovery time, for stores nearing disk limits, and for unusually long pauses in a task’s processing. These give early warning of problems that otherwise appear only as delayed outputs.
19Design Patterns and Anti-Patterns
Approaches that keep state healthy, and the traps that quietly cause pain.
Patterns worth adopting
Keep values small
Store compact summaries such as counts and totals instead of full event histories. Small values keep stores, changelogs and restores manageable.
Give every store a stable name
Explicit names protect changelog topics and make monitoring and querying straightforward across releases.
Plan expiry from the start
Use windows and retention, or explicit deletion, so stores do not grow forever. Decide when each piece of state stops being useful.
Run with standbys and durable local volumes
Combine standby replicas with persistent volumes so both failures and routine restarts recover quickly.
Anti-patterns to avoid
Problem
Storing entire event histories or very large lists as the value for a key, for example every order a customer has ever made.
Why It’s Harmful
Each update rewrites the whole large value, slowing the store, bloating the changelog and making restores and network transfers expensive. Values may also exceed size limits.
Correct Approach
Store compact aggregates, or spread the data across more keys such as customer plus order, and keep the detailed history in a topic or an external system built for it.
Problem
Letting stores grow forever with no expiry, such as remembering every identifier ever seen for deduplication.
Why It’s Harmful
Disk usage grows without limit, restores get slower each month, and eventually the application cannot recover in an acceptable time.
Correct Approach
Use a windowed store with a sensible retention, or delete keys when they are no longer needed.
Problem
Running stateful applications on machines whose local disks are wiped at every restart, with no standbys.
Why It’s Harmful
Every deployment forces a complete restore from the changelog for every task, causing long pauses in processing and large bursts of network traffic.
Correct Approach
Use durable volumes for the state directory, configure standby replicas, and roll out changes gradually.
Problem
Using a state store as a general-purpose database for ad hoc searching and reporting.
Why It’s Harmful
Stores are designed for key lookups and key ranges. Complex searches across many fields are slow or impossible, and the design leads to awkward workarounds.
Correct Approach
Use interactive queries for key-based lookups, and send the results to a purpose-built database when you need rich querying.
Problem
Keeping important state in ordinary program variables or files outside the state store.
Why It’s Harmful
That state is invisible to the changelog. After a crash or a task move, it is lost or inconsistent with the store, causing wrong results that are very hard to trace.
Correct Approach
Keep anything that matters to correctness inside a registered state store.
20Choosing the Right Store: A Decision Flow
A short path from your requirement to the store that fits.
flowchart TD
S["What do you need to remember?"] --> Q1{"Small reference data needed everywhere?"}
Q1 -- "Yes" --> G["Global store"]
Q1 -- "No" --> Q2{"Depends on time?"}
Q2 -- "No" --> KV["Key-value store"]
Q2 -- "Yes" --> Q3{"Fixed time slices?"}
Q3 -- "Yes" --> W["Window store"]
Q3 -- "No, activity bursts" --> SS["Session store"]
KV --> Q4{"Large or fast restart needed?"}
W --> Q4
SS --> Q4
Q4 -- "Yes" --> P["Persistent store with standbys"]
Q4 -- "No, small and hot" --> M["In-memory store"]
Identify what must be remembered
List the exact values needed per key, such as a count, a total or the latest record, and keep them as small as possible.
Decide the shape
Choose key-value, window, session, versioned or global depending on whether time or full replication matters.
Decide the storage
Pick persistent for large or restart-sensitive state, in-memory for small, easily rebuilt data.
Plan resilience
Set the number of standby replicas, the state directory location and the delivery guarantee that fits the business impact of errors.
Plan size, expiry and monitoring
Estimate disk and memory, set retention, name the store, and create dashboards and alerts before going live.
21Best Practices and Common Mistakes
A quick reference to keep beside you when designing stateful applications.
Best Practices
- Name every store explicitly and keep names stable
- Store compact values, not growing collections
- Set retention and cleanup for windowed and long-lived data
- Use persistent stores for large state
- Configure standby replicas for large or critical stores
- Keep the state directory on durable storage
- Bound memory used by the storage engine
- Choose exactly-once when duplicates would be costly
- Test failure and restore time before production
Common Mistakes
- Disabling the changelog to save space
- Ignoring restore time for large stores
- Changing input partition counts for stateful apps
- Leaving stores with generated names
- Forgetting the memory used outside the heap
- Putting huge topics in a global store
- Treating a store as a full database
- Keeping critical state outside registered stores
- Ignoring internal topics in security reviews
Most painful state store incidents are slow-moving: growth without expiry, restores that gradually take longer, and memory that climbs a little each week. Regular reviews of size and restore time catch them long before an outage.
A well-run shop tidies its notebooks, keeps duplicates in a safe place, knows how long it takes to recreate a lost one, and never lets the pile of old pages grow unnoticed. State store care works the same way.
22Real-World Scenarios
Five common situations show how the ideas fit together.
Scenario 1: Running order totals for an online shop
An online store keeps the running total spent per customer. A key-value store keyed by customer ID holds the totals, backed by a compacted changelog. Interactive queries let the website show the total instantly. Exactly-once is used because the totals drive loyalty rewards.
Scenario 2: Fraud detection with time windows
A payments company counts card transactions per card per ten minutes. A window store holds the counts, and a rule raises an alert when a card exceeds a limit. Retention is set just above the window plus a grace period so the store stays small.
Scenario 3: Website visit analysis
A media site groups a reader’s page views into visits that end after thirty minutes of inactivity. A session store keeps each visit, merging sessions when a late event connects them. The output feeds daily engagement reports.
Scenario 4: Enriching events with product data
A logistics platform enriches shipment events with product details from a small catalogue topic. A global store gives every instance a full copy, so shipments can be enriched locally without repartitioning the busy stream.
Scenario 5: Deduplicating device events
Connected devices sometimes resend readings. The application remembers recent event identifiers in a window store and drops repeats. Old identifiers expire automatically, so the store size stays steady.
| Scenario | Store Shape | Key Concern |
|---|---|---|
| Order totals | Key-value | Correct counts, fast queries |
| Fraud detection | Window | Retention and late events |
| Visit analysis | Session | Merging sessions |
| Product enrichment | Global | Keeping reference data small |
| Deduplication | Window | Expiry to bound size |
23Frequently Asked Questions
No. A topic is a durable, ordered log stored on Kafka brokers. A state store is a local key-value database next to your application. They are linked because every change to a store is also written to a topic, called the changelog.
No. The default persistent store uses an embedded engine that ships with Kafka Streams. There is nothing extra to install or run.
Another instance takes over the task and rebuilds the store from the changelog topic before processing continues. If a standby replica exists, the takeover is much faster.
Not directly. Access is through interactive queries, where your own application exposes an endpoint that reads the store, forwarding requests to the instance that owns the requested key.
The record cache merges rapid updates to the same key and emits only the latest. The final values are unaffected. Only intermediate steps are combined.
One logical store for each stateful operation, and each is split into one shard per task. An application with three stateful steps and eight partitions has three logical stores and twenty-four shards.
Not safely for a stateful application. State is tied to the key-to-partition mapping. Create a new topic and a new application version, and rebuild the state.
When the data is small, needed by every instance, and you want to avoid matching partitions or repartitioning. Avoid it for large or rapidly changing data.
For key-value stores, yes, keeping the latest value per key. For windowed stores, the changelog also has a retention limit so expired windows disappear over time.
A standby is a long-term backup copy kept for fast failover. A warm-up replica is a temporary copy built on a new instance so an active task can move there smoothly, after which it becomes the active store.
Only when duplicate effects would be harmful, such as money or stock. It adds overhead and can lengthen recovery, so approximate or repeat-safe workloads can use the simpler guarantee.
Most likely it is restoring large stores from the changelog because local files were lost or invalidated. Use durable volumes, standby replicas and gradual rollouts to reduce this.
24Summary and Key Takeaways
A state store is the local, embedded memory of a Kafka Streams application. It lets stateful steps such as counting, joining, windowing and deduplicating read and write remembered values at local speed. The state is split into shards, one per task, and every change is mirrored to a compacted changelog topic in Kafka, which is what makes the design fault tolerant. When a machine fails, another instance rebuilds the store from the changelog, and standby and warm-up replicas shorten the wait.
Good state store design means choosing the right shape and storage, keeping values small, naming stores explicitly, planning expiry and sizes, securing every place the data lives, and watching restore time, memory and growth. If you remember one sentence, let it be this: the local store makes your application fast, and the changelog makes it safe.
Key Takeaways
- State enables stateful logic — counts, joins, windows and deduplication need remembered values, and state stores hold them locally next to the code.
- One shard per task — the number of tasks equals the number of input partitions, and each task owns its own slice of every store it uses.
- The changelog makes state durable — every store change is also written to a compacted Kafka topic, so a lost store can always be rebuilt.
- Choose shape and storage on purpose — key-value, window, session, versioned and global stores fit different needs, and persistent stores suit large or restart-sensitive state.
- Recovery time is the main risk — use durable local volumes, standby replicas and warm-up replicas to avoid long pauses after failures and scaling.
- Plan partitions and names early — changing input partition counts or store names later breaks the link between keys, shards and changelogs.
- Size, secure and monitor — bound memory, set retention, protect local files and internal topics, and watch restore time, store size and rebalances.