Changelog Topics in Kafka

Changelog Topics in Kafka

How Kafka keeps a durable, replayable backup of an application's local state as a compacted log of updates, so that a crashed or moved stream processing task can rebuild exactly what it knew, and what happens at every step of that rebuild.

Imagine a shopkeeper who keeps a small notebook on the counter with today’s stock: twelve apples, five loaves, three jars of jam. The notebook is fast to check, so customers are served quickly. But the notebook lives on the counter, and if a fire destroys the shop, the stock levels vanish with it. A wise shopkeeper therefore also phones every change to a head office, which writes each one in a permanent ledger: “apples down by one”, “loaves up by ten”. If the shop burns down, a new shopkeeper can read the ledger from the top, replay each line, and end up with the same notebook, page for page.

A stream processing application has exactly this need. It keeps counts, totals, and lookup tables in fast local storage so it can answer in microseconds, yet it must survive crashes and machine moves without forgetting anything. Kafka’s answer is the changelog topic: a special topic that records every change to the local state, in order, forever compacted down to the latest facts. This tutorial explains what changelog topics are, how they are written and read, how they stay small, how they rebuild state after failure, and how to operate them safely.

1The Problem: State That Lives on One Machine

Before meeting the solution, feel the problem that makes it necessary.

Stateless versus stateful processing

Some stream processing is stateless: each record is handled entirely on its own. Converting a temperature from Celsius to Fahrenheit, or dropping records that fail a filter, needs no memory of past records. If the application crashes and restarts, it simply carries on with the next record, and nothing is lost.

Much more useful processing is stateful. Counting page views per user, keeping the latest balance per account, joining an order with the customer’s profile, or computing an average over the last five minutes all require the application to remember something about the past. That memory is called state, and it has to be stored somewhere.

Simple Analogy

A cashier who only scans items and reads out prices needs no memory. A cashier who keeps a running total for each customer in the queue needs a notepad. The notepad is the state, and losing it mid-shift means every customer’s total is gone.

Why local storage

Reaching across the network to a remote database for every single record would be slow, and it would make the processing speed depend on that database. So stream processing libraries keep state next to the code, in an embedded store on the local disk, or in memory. A lookup then takes microseconds. This design is fast and scales well, because each application instance owns the state for only its share of the data.

The danger of local storage

Local state has a fatal weakness: it lives and dies with the machine. If the machine fails, the disk may be lost. If the cluster manager moves the work to another machine, the state stays behind. If the application is scaled out, some state must move to the new instance. Without a plan, any of these events means the counts restart at zero or the lookup table is empty, and the results downstream are silently wrong.

Risk 1

Machine failure

A dead disk takes the state with it, and nothing on another machine knows what the totals were.

Risk 2

Rescheduling

Container platforms move workloads between hosts all the time, and a fresh container starts with an empty disk.

Risk 3

Scaling out or in

Adding an instance means some partitions, and their state, must migrate. Removing one means its state must be picked up by others.

Risk 4

Rolling upgrades

Restarting instances one by one repeatedly interrupts state ownership, and each restart must not lose anything.

!
Common Misconception

Some people think that because the input data is safe in Kafka, the results can always be recomputed. That is true in principle, but replaying weeks of input to rebuild a counter can take hours or days. A changelog makes recovery a matter of minutes, and often seconds.

2What Is a Changelog?

A changelog is not a copy of the state. It is the story of how the state came to be.

State as the sum of its history

Consider a table that holds the number of visits per user. At any moment, the table shows only the current numbers: Asha has 7, Bilal has 3. But there is another way to describe the same table: as a list of events that produced it. “Asha goes to 1, Bilal goes to 1, Asha goes to 2, Asha goes to 3, Bilal goes to 2”, and so on. If you replay that list from the start, you arrive at the current table. The list is the changelog.

flowchart LR
    A["Event: Asha visits"] --> T1["Table: Asha 1"]
    T1 --> C1["Changelog record: Asha = 1"]
    B["Event: Bilal visits"] --> T2["Table: Asha 1, Bilal 1"]
    T2 --> C2["Changelog record: Bilal = 1"]
    D["Event: Asha visits"] --> T3["Table: Asha 2, Bilal 1"]
    T3 --> C3["Changelog record: Asha = 2"]
    C1 --> LOG["Changelog topic: Asha=1, Bilal=1, Asha=2"]
    C2 --> LOG
    C3 --> LOG
        
FIG 1 — Every update to the table appends one record to the changelog

The stream-table duality

This idea is often called the duality of streams and tables. A stream of changes can be turned into a table by keeping the latest value for each key. A table can be turned back into a stream of changes by emitting a record every time a row changes. They are two views of the same information, and each can be rebuilt from the other. A changelog topic is simply the stream view of a state store, stored durably in Kafka.

Simple Analogy

A bank statement lists every transaction, and the balance at the bottom is the sum of them all. Given the statement you can compute the balance, and given a series of balances you can work out the transactions. The statement is the changelog, and the balance is the table.

What each record contains

A changelog record carries the key of the row that changed and the new value for that key. It does not say “add one”. It says “the value for Asha is now 2”. This choice is important, because it makes each record self-contained. To find the current value of a key, you only need the latest record for it, and older records for the same key are redundant. That property is what makes compaction, explained later, possible.

i
Key Insight

Changelog records store the resulting value, not the operation that produced it. Replaying the same records twice, or replaying only the latest record per key, leads to the same final table. That is what makes the changelog safe to replay after any failure.

3Core Vocabulary You Need

Each term below is defined once here and then used consistently for the rest of the tutorial.

Term 1

State store

A local key-value store that a stream processing task uses to remember information between records.

Term 2

Changelog topic

An internal Kafka topic that records every update to a state store, one record per update.

Term 3

Log compaction

A background clean-up that keeps only the latest record for each key, so the log stays as small as the live data.

Term 4

Tombstone

A record with a key and an empty value, meaning “this key is deleted”. Compaction eventually removes the key entirely.

Term 5

Task

The unit of work in a stream application, responsible for one partition of the input and its own state stores.

Term 6

Restoration

Rebuilding a state store by reading its changelog topic from the start, or from a saved position, up to the end.

Term 7

Checkpoint file

A small file on the local disk recording how far the local store has caught up with its changelog, to shorten restoration.

Term 8

Standby replica

A passive copy of a task’s state kept warm on another instance by continuously reading the changelog.

Term 9

Warm-up replica

A temporary copy created while a task is being moved, so the destination catches up before taking over.

Term 10

Application ID

The unique name of a stream application. It prefixes the names of all its internal topics and identifies its consumer group.

Term 11

Windowed store

A state store whose keys also include a time window, used for counts or aggregates over periods such as five minutes.

Term 12

Internal topic

A topic created and managed by the stream library itself, such as changelog and repartition topics, and not meant for other applications.

i
Naming Note

The word changelog is also used in other places. Data capture tools describe the stream of changes from a database as a changelog, and a compacted topic that holds the latest value per key is often described the same way. This tutorial focuses on the state store changelog in stream processing, and later chapters show how the same idea appears elsewhere in Kafka.

4The Big Picture: A Fast Store Backed by a Durable Log

The design combines the speed of local storage with the safety of a replicated log.

Two copies, two jobs

Every stateful task keeps its state in two places. The local state store is the working copy: it answers reads and writes in microseconds and lives on the instance’s disk or in memory. The changelog topic is the durable backup: it lives in the Kafka cluster, replicated across brokers, and survives the loss of any instance. Each time the task updates the local store, it also appends the update to the changelog.

Simple Analogy

A chef keeps a whiteboard with today’s ingredient counts, and updates it constantly. Every time she changes the board, an assistant writes the change in a bound book locked in the office. The board is fast to read, and the book is safe from spills and thieves. If the board is wiped, the book restores it.

flowchart TD
    subgraph Instance["Application instance"]
        IN["Input record"] --> PR["Processing logic"]
        PR --> ST["Local state store: fast reads and writes"]
        ST --> CL["Changelog writer"]
    end
    CL --> TOP["Changelog topic in Kafka: replicated, compacted"]
    TOP --> RES["After failure: another instance restores the store from the topic"]
    RES --> ST2["Rebuilt local store on new instance"]
        
FIG 2 — The working copy is local, the backup is a replicated Kafka topic

Why this arrangement is smart

1

Reads never touch the network

The common operation, looking up the current value for a key, is served from local storage. There is no remote call in the hot path.

2

Writes are cheap appends

Writing to the changelog is a sequential append to a Kafka log, the operation Kafka is best at. It adds little cost per update.

3

Durability comes from replication

The changelog is an ordinary replicated topic, so the backup enjoys the same protection as any other Kafka data.

4

Recovery is just reading a topic

No special backup system, snapshot service, or external database is needed. Any instance can rebuild the state by consuming the changelog.

5

State moves with the work

When a task is reassigned to another instance, that instance rebuilds the state from the topic. Ownership can move freely between machines.

“Keep the working copy close and the truth in the log.”

5Architecture and Components

Several parts of the stream library and of Kafka cooperate to keep the local store and the changelog in step.

flowchart LR
    subgraph Inst["Stream application instance"]
        TASK["Stream task for partition 2"]
        STORE["State store: embedded database on disk or in memory"]
        WRAP["Logging layer around the store"]
        COL["Record collector: producer"]
        RC["Restore consumer"]
        CHK["Checkpoint file"]
    end
    subgraph Cluster["Kafka cluster"]
        CLT["Changelog topic, partition 2"]
        OUT["Output topics"]
        OFF["Offsets topic"]
    end
    TASK --> WRAP
    WRAP --> STORE
    WRAP --> COL
    COL --> CLT
    COL --> OUT
    COL --> OFF
    CLT --> RC
    RC --> STORE
    RC --> CHK
        
FIG 3 — The parts that connect a task’s state store to its changelog partition

The roles, one by one

Component

Stream task

Processes one partition of the input and owns the state for the keys in that partition, giving it one changelog partition per store.

Component

State store

The embedded key-value database. By default it uses a disk-based engine, and an in-memory version is also available.

Component

Logging layer

A wrapper around the store. Whenever the task puts or deletes a key, the wrapper also forwards the change to the changelog.

Component

Record collector

The producer of the task. It sends changelog records alongside the task’s output records, using the same producer.

Component

Restore consumer

A dedicated consumer that reads changelog partitions during restoration and feeds the records into the local store.

Component

Checkpoint file

Records the last changelog offset that is known to be reflected in the local store, letting a restart skip most of the replay.

!
Common Mistake

Treating changelog topics as ordinary topics that other applications may read or write. They are internal, the format may include library-specific details, and writing to them from outside can corrupt the state that the application rebuilds from them.

6Anatomy of a Changelog Topic

Every changelog topic follows the same conventions for its name, partitions, and settings.

The name

A changelog topic is created automatically by the stream library, and its name follows a fixed pattern: the application ID, then the name of the state store, then the word changelog, all joined by hyphens. For an application called page-views-app with a store named views-per-user, the topic becomes page-views-app-views-per-user-changelog. Because the application ID is part of the name, two applications never share changelog topics by accident, unless someone gives them the same ID.

The partitions

The changelog has exactly as many partitions as the input the store is built from. If a task processes partition 2 of the input, its store’s changelog records go to partition 2 of the changelog. This alignment is deliberate. It means one task owns one input partition and one changelog partition per store, and moving a task means moving a matched set of partitions. The stream library also checks these counts at startup, and refuses to run if the changelog has a different number of partitions than expected.

flowchart LR
    subgraph In["Input topic: 3 partitions"]
        I0["Partition 0"]
        I1["Partition 1"]
        I2["Partition 2"]
    end
    subgraph Tasks["Three tasks"]
        T0["Task 0"]
        T1["Task 1"]
        T2["Task 2"]
    end
    subgraph CL["Changelog topic: 3 partitions"]
        C0["Partition 0"]
        C1["Partition 1"]
        C2["Partition 2"]
    end
    I0 --> T0
    I1 --> T1
    I2 --> T2
    T0 --> C0
    T1 --> C1
    T2 --> C2
        
FIG 4 — One input partition, one task, one changelog partition, per store

The settings

SettingTypical value for a changelogReason
Cleanup policyCompactKeep only the latest record for each key
Cleanup policy for windowed storesCompact and deleteOld windows must eventually disappear, so time-based deletion is added
Replication factorThree in productionThe changelog is the only durable copy, and needs full protection
PartitionsSame as the input partitionsKeeps tasks and their state aligned
Retention timeNot used for plain stores, window retention plus a margin for windowed storesPrevents unbounded growth of window state
Segment size and ageModerateCompaction only works on closed segments, so smaller segments compact sooner
i
Created for you

The stream library creates changelog topics with sensible defaults when the application first starts. The application needs permission to create topics, or an administrator must create them in advance with the exact expected names and partition counts.

What a record looks like

The key of each changelog record is the serialized key of the store row. The value is the serialized value, which in many store types is stored together with a timestamp so that time-aware operations remain correct after restoration. The timestamp of the changelog record itself is normally taken from the input record that caused the update, so the changelog remains a faithful timeline of the input. A record with an empty value is a tombstone, which signals a deletion.

7The Write Path: From an Update to a Changelog Record

Follow one input record as it changes the state and leaves a footprint in the changelog.

1

Input record arrives

The task reads a record, for example a page view event for user Asha, from its input partition.

2

Read the current value

The processing logic looks up Asha in the local store and finds the count 6.

3

Compute and put the new value

The logic adds one and puts the new count, 7, into the store under the key Asha.

4

Logging layer forwards the change

The wrapper around the store takes the put and hands the key and new value to the record collector.

5

Producer appends to the changelog

The record collector sends the record to the matching partition of the changelog topic, where the partition leader appends it.

6

Commit ties it all together

At the next commit, the library flushes the store and the producer, and commits the input offsets, so the state, the output, and the progress agree.

sequenceDiagram
    participant In as Input partition
    participant T as Task
    participant S as Local store
    participant P as Record collector (producer)
    participant C as Changelog partition
    In->>T: Page view for Asha
    T->>S: Get Asha
    S-->>T: 6
    T->>S: Put Asha = 7
    S->>P: Forward change (Asha, 7)
    P->>C: Append record (Asha, 7)
    C-->>P: Acknowledged after replication
    T->>T: Commit interval reached
    T->>S: Flush store
    T->>P: Flush producer, commit input offsets
        
FIG 5 — One update flows from input, through the store, into the changelog

Batching and the commit interval

The library does not wait for a changelog acknowledgement after every single update. Records are batched by the producer and sent in the background, and acknowledgements are awaited when the task commits. The commit interval therefore controls how much work is in flight at any time. A longer interval allows bigger batches and higher throughput, and a shorter interval gives smaller batches and tighter durability.

Caching before the changelog

Stateful operations such as aggregations often place a small in-memory cache in front of the store. If the same key is updated many times between commits, only the last value needs to reach the store and the changelog. This reduces write volume considerably for hot keys, and it also reduces the number of output records emitted downstream. The cache is flushed at each commit, so the changelog always catches up at commit boundaries.

!
Ordering matters

The store is updated first and the changelog second, both in the same task thread. If the process crashes between the two, the local store may be ahead of the changelog. The library handles this by never trusting local state after an unclean shutdown in the strictest processing mode, as later chapters explain.

8Log Compaction: Keeping Only the Latest

Without compaction, a changelog would grow forever. With it, the log stays roughly as large as the live state.

The growth problem

If Asha’s counter is updated a million times, the changelog holds a million records for the key Asha. Yet only the last one matters for restoring the state. Keeping all million would waste disk space and make restoration slow, since a restoring instance would have to read every one of them. A plain time-based retention would not help either, because deleting old records could delete the only record for a key that never changes.

What compaction does

Log compaction is a broker background process that scans closed log segments and, for each key, keeps only the most recent record, removing older ones. The log’s offsets are preserved for the records that remain, so consumers still see a consistent order, but many offsets simply no longer exist. The end result is a log that holds, at minimum, the latest value for every key that has not been deleted.

flowchart TD
    subgraph Before["Before compaction"]
        B1["offset 0: Asha = 1"]
        B2["offset 1: Bilal = 1"]
        B3["offset 2: Asha = 2"]
        B4["offset 3: Chen = 1"]
        B5["offset 4: Asha = 3"]
        B6["offset 5: Bilal = 2"]
    end
    Before --> CMP["Log cleaner keeps latest record per key"]
    CMP --> After
    subgraph After["After compaction"]
        A1["offset 3: Chen = 1"]
        A2["offset 4: Asha = 3"]
        A3["offset 5: Bilal = 2"]
    end
        
FIG 6 — Compaction removes superseded records and keeps the latest per key
Simple Analogy

Think of a phone book where every time someone changes their number, a new line is added. Over the years the book gets huge. A clean-up removes each old line for anyone who has a newer line, leaving one current number per person. The book is smaller, and it answers every question the long version could.

What compaction guarantees and what it does not

Guaranteed

  • The latest record for every key is never removed by compaction
  • Order of remaining records is preserved
  • Consumers reading from the start see at least the final value of each key
  • Log size trends toward the size of the live data plus a small tail

Not guaranteed

  • Immediate removal of old records, since compaction runs in the background
  • Compaction of the currently active segment, which is left alone
  • That a reader sees every intermediate value, since some are removed
  • A predictable moment when a given record disappears

The tuning knobs

SettingWhat it controlsEffect of raising it
Minimum cleanable dirty ratioHow much of the log must be superseded before cleaning startsLess frequent cleaning, more wasted space and slower restores
Segment age or sizeWhen the active segment is closed and becomes eligibleLonger wait before recent records are compacted
Minimum compaction lagHow long a record must sit before it can be compactedKeeps recent history available longer for readers
Delete retention timeHow long tombstones are kept after compactionLonger window for slow readers to notice deletions
i
Restore time follows compaction

A restoring instance must read every record still in the changelog. A well-compacted log holds roughly one record per live key, so restoration reads about as many records as there are keys. A poorly compacted log can hold many times more, and restoration time grows to match.

9Tombstones: Recording Deletions

State is not only created and updated. Sometimes keys must disappear, and the changelog has to say so.

What a tombstone is

When the application deletes a key from the store, the logging layer appends a record with that key and an empty value to the changelog. This is a tombstone. It tells any reader replaying the changelog that the key is gone. During restoration, the library sees the tombstone and removes the key from the rebuilt store, exactly as the original deletion did.

The life of a tombstone

1

Key is deleted

The application removes the key, and a tombstone is appended to the changelog after the key’s last real value.

2

Compaction removes old values

The cleaner discards earlier records for that key, leaving only the tombstone.

3

Tombstone lingers for a while

The tombstone stays for a configured period, by default about a day, so slow readers can learn of the deletion.

4

Tombstone is removed

After that period, the tombstone itself is deleted, and the key no longer appears anywhere in the log.

!
A subtle trap

A reader that is offline for longer than the tombstone retention period may miss the deletion entirely. It could then keep a stale copy of a key that no longer exists in the log. State restoration inside a healthy application always reads from a consistent starting point, but external tools that cache data from a compacted topic must consider this window.

Deletions and unbounded growth

A store whose keys are never deleted grows without limit, and so does its changelog. An application that tracks one entry per user session, for example, accumulates entries for every session ever seen unless something removes them. Good designs remove entries when they are no longer needed, by emitting tombstones or by using windows that expire on their own. Without this discipline, the changelog and restoration time grow steadily for months until they become a problem.

Session tracking

Remove a session entry when the session closes or a timeout passes, and the changelog gets a tombstone, so restoration never rebuilds finished sessions.

Deduplication tables

Keep recent identifiers only. Expire old ones with windows, so the table and its changelog stay small.

Customer lookup tables

Deletions in the source system arrive as tombstones in the input topic. They propagate to the store and to its changelog, and compaction eventually removes the data entirely.

10Windowed Stores and Retention

Time-based state has a natural expiry, and the changelog must know about it.

Windows add time to the key

A windowed aggregation, such as “page views per user per five minutes”, stores one row for each user and each window. The store’s key is really the pair of user and window start time. The changelog records these composite keys, so every window of every user is a separate key with its own latest value.

Why compaction alone is not enough

Windows end. After a window closes and its allowed lateness passes, its row will never change again, and nobody needs it after the retention period. But compaction only removes superseded records, not finished ones. If the changelog used compaction alone, closed windows would stay forever, and the log would grow with every new window.

flowchart TD
    A["Window row updated as events arrive"] --> B["Window closes, late arrivals still allowed for a grace period"]
    B --> C["Window becomes final"]
    C --> D{"Older than window retention plus margin?"}
    D -->|"No"| E["Row stays in store and changelog"]
    D -->|"Yes"| F["Local store drops the row"]
    D -->|"Yes"| G["Changelog segments older than the retention are deleted by the broker"]
        
FIG 7 — Windowed state expires by time, not only by being superseded

Compact and delete together

For windowed stores, the changelog uses both cleanup policies at once: compaction to remove superseded values, and time-based deletion to remove old windows. The retention time is set to the window retention of the store plus an extra margin, by default about a day, so that the changelog never deletes data the local store might still legitimately need. This is a careful balance: too short, and a restore could lose windows that still matter, and too long, and the log holds pointless history.

Store kindCleanup policy on changelogGrowth behavior
Plain key-value storeCompactBounded by the number of live keys
Windowed storeCompact and deleteBounded by keys times windows within retention
Session-windowed storeCompact and deleteBounded by active and recent sessions within retention
i
Sizing rule of thumb

The number of rows in a windowed store is roughly the number of distinct keys multiplied by the number of windows kept. Halving the retention roughly halves both the local store and the changelog, and also halves restoration time.

11Restoration: Rebuilding State After a Failure

This is the moment the changelog earns its keep. Follow it from the failure to the first processed record.

When restoration happens

Trigger

Instance crashes and restarts

The local disk may survive, in which case only a small tail must be replayed, or it may be gone, in which case the whole store is rebuilt.

Trigger

Task moves to another instance

After a rebalance, the new owner of a task needs the state for its partitions and starts from whatever local copy it has.

Trigger

New instance joins

Some tasks migrate to the newcomer, which begins with empty local state for them.

Trigger

Corrupted local state

If the library detects that local state cannot be trusted, it discards it and rebuilds from the changelog.

The restoration steps

1

Task is assigned

After a rebalance, the instance receives a task and finds that its store is missing or behind.

2

Find the starting point

The library checks the checkpoint file for the last known changelog offset. If none exists, it starts at the beginning of the changelog partition.

3

Find the end point

It asks the broker for the current end offset of the changelog partition. This is the target the restore must reach.

4

Read and apply records

The restore consumer reads records in batches and writes each key and value into the local store, removing keys for tombstones. It writes directly, without producing anything back to the changelog.

5

Reach the end and switch

When the restore position reaches the recorded end offset, the store matches the changelog. The task moves to the running state and begins processing input.

sequenceDiagram
    participant App as Instance
    participant CP as Checkpoint file
    participant B as Broker
    participant S as Local store
    App->>CP: Read last restored offset
    CP-->>App: Offset 4,200,000 or none
    App->>B: Get end offset of changelog partition
    B-->>App: End offset 4,350,000
    loop Until end offset reached
        App->>B: Fetch next batch from changelog
        B-->>App: Records
        App->>S: Write keys and values, delete for tombstones
    end
    App->>CP: Save new checkpoint
    Note over App: Task starts processing input
        
FIG 8 — The restore loop from checkpoint to the end of the changelog
!
Processing waits for restoration

While a task’s state is being restored, that task does not process input. For large stores this pause can last minutes or longer, during which the partition’s consumer lag grows. This is the price of correctness, since processing with incomplete state would produce wrong results.

12Checkpoints and Shortcuts

Reading a whole changelog on every restart would be wasteful. Checkpoints let a restart resume where it left off.

What the checkpoint file records

When an instance shuts down cleanly, it writes a small checkpoint file into the state directory. For each store, the file records the changelog offset up to which the local data is known to be complete. On the next start, if the local store is still on disk and the checkpoint is present, the restoration begins at that offset rather than at the start of the log. Only the records written after the checkpoint need to be replayed, which is often a tiny fraction of the total.

Simple Analogy

A reader who puts a bookmark in a long novel does not start again from chapter one after a break. The bookmark is the checkpoint, and it tells them exactly which page to continue from.

flowchart TD
    A["Instance starts task"] --> B{"Local store and checkpoint present?"}
    B -->|"Yes"| C["Restore from checkpoint offset to end of changelog"]
    B -->|"No local store"| D["Restore from the beginning of the changelog"]
    B -->|"Store present, checkpoint missing or untrusted"| E["Discard local store, restore from the beginning"]
    C --> F["Small replay, fast start"]
    D --> G["Full replay, slowest start"]
    E --> G
        
FIG 9 — The starting point of a restore depends on what survived locally

Why the state directory matters

The location of the local state is set by a configuration value for the state directory. If that directory is on storage that survives restarts, such as a persistent volume, restarts are fast. If it is on temporary storage that disappears with the container, every restart becomes a full restoration. On container platforms, this single decision often decides whether deployments feel instant or take a long time.

State directory locationRestart behaviorTrade-off
Persistent volume that follows the instanceCheckpoint found, short replayNeeds stable volume management and identity per instance
Local disk of a fixed hostFast if the same host is reusedFast only while the workload stays on that host
Temporary container storageFull restore every timeSimplest to operate, slowest to recover
i
The changelog is always the safety net

The checkpoint and local disk are only optimizations. If they are missing or untrusted, the library falls back to the changelog, which is always complete. Correctness never depends on the local disk, only speed does.

Fast bulk loading

When restoring a large amount of data into the embedded database, the library switches the store into a mode optimized for bulk writes, for example by postponing background compaction inside the database until the restore ends. This makes restoration considerably faster than normal writes, since the data arrives in order and needs no reads to check for existing values. After the restore finishes, the store returns to its normal mode.

13Standby Replicas and Warm-Up Replicas

Restoration takes time. Standby replicas remove most of that time by keeping a second copy always ready.

The idea

A standby replica is a copy of a task’s state stores that lives on a different instance, and keeps itself up to date by continuously reading the changelog. It does not process input or produce output. It only maintains the store. If the instance holding the active task fails, the group moves the task to the instance that holds its standby, which already has almost all the data. The remaining restoration is a few seconds of catch-up instead of minutes.

Simple Analogy

A relief pilot sits in the cockpit jump seat, watching every instrument. If the captain becomes unwell, the relief pilot takes over immediately, with full knowledge of the flight so far. Compare that with calling a pilot from home, who must first be briefed on everything.

flowchart LR
    subgraph A["Instance A"]
        AT["Active task 0: processes input, writes changelog"]
    end
    subgraph B["Instance B"]
        SB["Standby task 0: reads changelog, keeps copy warm"]
    end
    AT -->|"appends updates"| CL["Changelog partition 0"]
    CL -->|"continuous reads"| SB
    AT -.->|"instance A fails"| SB
    SB -->|"promoted to active with almost no restore"| NEW["Active task 0 on instance B"]
        
FIG 10 — A standby copy shortens failover from a full restore to a short catch-up

The cost and the benefit

Benefits

  • Failover in seconds rather than minutes for large stores
  • Smaller processing pause and less lag buildup after a failure
  • Smoother rolling upgrades, since tasks can move to warm copies
  • Less load on the changelog during a crisis

Costs

  • Extra disk space for each replica of each store
  • Extra network and consumer load to keep replicas current
  • More instances or capacity needed to host the copies
  • No effect on data safety, which the changelog already provides

Warm-up replicas during moves

A similar mechanism helps when a task must move, for example onto a newly added instance. Rather than stopping the task and restoring from scratch on the destination, the library creates a temporary warm-up replica on the destination, lets it catch up in the background while the old owner keeps processing, and only then completes the move. A periodic check, called a probing rebalance, decides when the destination has caught up close enough to the end of the changelog. The gap it must close is governed by an acceptable recovery lag setting, and the number of simultaneous warm-ups is limited so that the changelog and the network are not flooded.

1

New instance joins

The group decides some tasks should move to it, but its local state is empty.

2

Warm-up begins

The new instance restores the state of those tasks in the background. The old owners keep running.

3

Probing rebalance checks progress

At regular intervals, the group asks whether each warm-up is within the acceptable lag of the changelog end.

4

Hand over

When the copy is caught up, the task moves. The new owner needs almost no restoration, and processing barely pauses.

i
Latency-sensitive applications

Applications with strict recovery time targets typically set at least one standby replica. Applications that can tolerate pauses, or whose state is tiny, often leave it at zero to save resources.

14Exactly-Once Processing and Changelogs

State updates must follow the same all-or-nothing rules as output records, otherwise recovery would double count.

The problem without atomicity

Suppose a task updates a counter, writes the new value to the changelog, and crashes before committing its input offsets. On restart, it reads the same input record again and increments the counter a second time. The state now reflects the record twice. In a counting application, this is a permanent error, because nothing will ever subtract the extra one.

Changelog writes inside the transaction

In exactly-once mode, the changelog writes, the output records, and the input offset commits are all part of a single transaction. If the transaction commits, the state change, the output, and the progress become visible together. If it aborts, the changelog records are hidden by an abort marker, the offsets do not move, and the input is read again. Restoration reads the changelog in committed-only mode, so aborted updates never reach a rebuilt store.

sequenceDiagram
    participant T as Task
    participant S as Local store
    participant P as Transactional producer
    participant CL as Changelog partition
    participant O as Output partition
    T->>P: Begin transaction
    T->>S: Update counter to 7
    S->>P: Changelog record (Asha, 7)
    P->>CL: Append, flagged transactional
    T->>P: Output record for downstream
    P->>O: Append, flagged transactional
    T->>P: Add input offsets, commit
    P->>CL: Commit marker
    P->>O: Commit marker
    Note over CL,O: State, output, and progress become visible together
        
FIG 11 — Changelog, output, and offsets share one transaction

Why local state cannot always be trusted after a crash

In exactly-once mode, the local store may contain updates from a transaction that never committed, because the store was written before the commit. If the instance crashes, its local disk holds state that includes those uncommitted changes, and the changelog does not. To stay correct, the library takes a cautious approach: after an unclean shutdown, it treats the local store as untrustworthy, discards it, and restores from the committed changelog. A clean shutdown writes a checkpoint and the local copy can be reused. This is why crashes cost more in exactly-once mode than in at-least-once mode, and why standby replicas are valuable there.

Processing modeState update and outputAfter an unclean crash
At least onceWritten independently, may repeat on replayLocal state may be reused, but replayed input may update it twice
Exactly onceAtomic with offsets in one transactionLocal state discarded, rebuilt from committed changelog, replay is safe
!
Trade-off to plan for

Correct counting after a crash is worth a longer restoration. But the longer restoration means recovery time is driven by store size, so keep stores lean, keep the changelog well compacted, and use standby replicas where recovery time matters.

15Shortcuts: Source Topics and Global Tables

Sometimes the changelog would only copy data that already exists in another topic. The library can skip the duplication.

Tables built straight from a topic

Suppose a table is defined directly on a topic that already holds the latest value per key, for example a compacted customer profile topic. The store’s changelog would contain exactly the same data as the source topic, doubling storage and write cost for no benefit. With a suitable optimization setting, the library skips the separate changelog and, when it needs to restore the store, reads the source topic itself.

flowchart TD
    A["Table defined directly on a compacted source topic"] --> B{"Source topic reuse optimization enabled?"}
    B -->|"No"| C["Separate changelog topic created, data duplicated"]
    B -->|"Yes"| D["No separate changelog, source topic serves as the changelog"]
    D --> E["Restoration reads the source topic"]
    C --> F["Restoration reads the changelog topic"]
        
FIG 12 — Reusing a source topic as the changelog saves space and writes
!
Conditions apply

This shortcut is only safe when the table is a direct, unmodified view of its source topic. Any transformation before the store breaks the equivalence, and the library then needs its own changelog. The source topic should also be compacted, or restoration would replay a very long history.

Global tables

A global table is fully replicated to every instance, so every instance can look up any key. It has no changelog of its own. Each instance restores it directly from the source topic, reading all partitions, and then keeps it current by continuing to read them. The trade-off is that every instance must hold the entire table, which is fine for small reference data and costly for large data.

Kind of tableData on each instanceBacked byBest for
Partitioned table with changelogOnly the partitions the instance ownsChangelog topic per storeLarge state, aggregations, joins on co-partitioned data
Partitioned table on source topicOnly the owned partitionsSource topic itself, with the optimizationLarge lookup tables that are direct views of a topic
Global tableAll partitions of the sourceSource topic itselfSmall reference data needed everywhere

16The Same Idea Elsewhere in Kafka

Once you recognize the pattern of a compacted log of state changes, you will see it in several other places.

Example 1

Consumer offsets

The internal offsets topic is a compacted log where the key is a group, topic, and partition, and the value is the committed position. It is the changelog of every consumer group’s progress.

Example 2

Transaction state

The transaction log is a compacted topic holding the latest state of each transactional identity, rebuilt by the coordinator when leadership moves.

Example 3

Metadata quorum

In the built-in metadata quorum, cluster decisions form a log with snapshots. Different mechanics, same idea: state is the result of replaying a durable log.

Example 4

Connector storage topics

Integration frameworks store connector configurations, offsets, and statuses in compacted topics, keyed by connector, so a restarted worker rebuilds them.

Example 5

Database change capture

Tools that capture row changes from databases write them into topics keyed by the primary key. Compacted, such a topic is the latest image of the table.

Example 6

Application-owned compacted topics

Teams create compacted topics for reference data, such as product catalogs or feature flags, which any service can load fully at startup.

“A compacted topic is a table that remembers how it got that way.”

Why the pattern keeps recurring

Log-based state has attractive properties for a distributed system: writes are simple appends, replication is already solved by Kafka, recovery is reading from the start, and any number of readers can build their own view. The changelog topics of stream applications are simply the same design applied to each application’s private state.

Bootstrapping a service from a compacted topic

A service reads a compacted topic from the beginning at startup to load a complete in-memory table, then keeps reading to stay current. This is a hand-made version of what state restoration does inside a stream application.

Materialized views for other systems

Sink connectors read a compacted topic and keep a search index or a database table in sync, treating each record as an upsert or a delete when a tombstone appears.

17Configuration and Defaults

A short list of settings controls how changelogs are created, written, and restored.

SettingRoleGuidance
application.idNames the application and prefixes internal topicsUnique per application, stable across versions
replication.factorReplication of internal topics such as changelogsThree in production. Recent versions can follow the broker default
num.standby.replicasNumber of warm copies per taskZero by default. Raise for faster failover
state.dirWhere local state livesUse durable storage if restart speed matters
commit.interval.msHow often state, output, and offsets are committedLong by default at-least-once, short in exactly-once mode
processing.guaranteeAt-least-once or exactly-onceExactly-once for correct counting across failures
acceptable.recovery.lagHow close a warm copy must be to count as caught upLower means stricter, higher means earlier handover
max.warmup.replicasLimit on simultaneous warm-upsKeep low to avoid flooding the changelog and network
probing.rebalance.interval.msHow often warm-up progress is checkedDefault of several minutes suits most workloads
topology.optimizationEnables shortcuts such as reusing source topicsEnable deliberately and test topology changes
Restore consumer prefix settingsTunes the consumer used for restorationRaise fetch sizes to speed up large restores
Internal topic override prefixSets topic-level settings for internal topicsUse for segment size or compaction tuning

Per-store control

The store definition lets a developer give the store a stable name, override changelog settings for that store, or turn logging off entirely. Turning logging off removes the changelog and with it the fault tolerance of that store: after a failure the store starts empty. This is acceptable only for caches that can be rebuilt cheaply from other sources, or for stores whose loss is truly harmless.

!
Never disable logging casually

Disabling the changelog for a store that holds important state means any crash, move, or rolling restart silently resets it. Reserve this for state you can recreate quickly and safely, and document the decision so nobody enables it by accident in a copy of the code.

Broker-side settings that matter

Minimum in-sync replicas

With three replicas and a minimum of two in sync, an acknowledged changelog write survives a broker failure. With weaker settings, an acknowledged update could vanish with a failed broker.

Unclean leader election

Keep it disabled. Otherwise an out-of-date replica could become leader of a changelog partition and silently discard recent updates.

Log cleaner capacity

Brokers need enough cleaner threads and memory to compact busy changelog partitions. If the cleaner falls behind, logs grow and restoration slows.

18Naming and Topology Evolution

The most damaging changelog problems come from innocent-looking code changes, not from crashes.

How names are generated

If a developer does not name a store or an operation explicitly, the library generates a name from the operation type and a running number, based on the position of the operation in the topology. A changelog name might therefore contain a number such as 0000000005. The number depends on how many operations came before it in the definition of the topology.

The trap

Now imagine a developer adds a new filter early in the topology. Every later operation shifts by one position, and the generated names change. On the next deployment, the application looks for changelog topics with the new names, finds none, and creates empty ones. The old changelogs, holding all the accumulated state, are now orphaned. The application starts with empty state, and every count starts again from zero.

flowchart TD
    A["Version 1: filter, then aggregate"] --> B["Aggregate store auto-named with number 3, changelog with number 3"]
    C["Version 2: new map added at the start"] --> D["Aggregate store now auto-named with number 4"]
    D --> E["Application looks for changelog number 4, finds none"]
    E --> F["Creates a new empty changelog, ignores the old one"]
    F --> G["State appears reset, counts restart from zero"]
        
FIG 13 — Unnamed operations make state depend on their position in the code
ANTI-PATTERN-01 Avoid
Problem

A production application relies on automatically generated names for its stateful stores and repartitioning steps.

Why It’s Harmful

Any change to the topology can silently rename internal topics. State is lost or split, and repartition topics can be abandoned with data still in them. The failure is quiet, and the symptoms appear only as wrong numbers.

Correct Approach

Give every stateful operation an explicit, stable name for its store, its grouping, and its repartition step. Treat those names as part of the public contract of the application, and never rename them without a migration plan.

Other changes that need care

ChangeEffect on the changelogSafe approach
Rename a storeNew changelog name, old one orphanedMigrate with a new application version and a rebuild, or keep the old name
Change key or value formatOld records cannot be read by new codeUse a compatible format, or reset and rebuild from source topics
Change the number of input partitionsPartition counts no longer matchCreate a new application with new topics and migrate
Add a new stateful operationNew empty changelog, no historyAccept the rebuild time, or seed the state deliberately
Change the application IDWhole new set of internal topics and groupPlan as a full migration, replay input from the start
i
Compare before you deploy

Print the description of the topology for the old and new versions and compare them. A difference in internal topic names is a red flag that state will not carry over, and it is far better to find it in a review than in production.

19Sizing and Capacity Planning

Changelog size is predictable once you know which numbers drive it.

The main drivers

Live keys
Number of distinct keys currently in the store
Row size
Average bytes per key and value pair
Update rate
Writes per second, which drives uncompacted tail size

After compaction, a plain changelog holds roughly one record per live key. Its size is therefore about the number of live keys multiplied by the average row size. On top of that sits an uncompacted tail: records written since the last cleaning, which grows with the update rate and the time between cleanings. Replication multiplies the total on disk across the cluster. Windowed stores add another factor: the number of windows kept.

ScenarioLive keysAverage rowApproximate compacted sizeMeaning for restoration
Small lookup table100,000200 bytesAbout 20 megabytesRestores in seconds
Per-user counters50 million100 bytesAbout 5 gigabytesRestores in minutes, spread across tasks
Windowed counts, 24 hourly windows10 million keys times 24120 bytesAbout 29 gigabytesNoticeable restore time, standbys advised
Unbounded session table with no expiryGrows every day500 bytesGrows without limitRestore time grows month by month

Spreading across tasks

The total state is divided among tasks, one per input partition. A store of five gigabytes across twenty partitions gives each task about 250 megabytes to restore, and tasks restore in parallel on different instances. More partitions therefore mean smaller pieces and faster parallel recovery, up to the point where per-partition overhead outweighs the benefit. When planning partition counts for a stateful application, think about recovery time as well as processing throughput.

i
Restore time estimate

Divide the per-task state size by the effective restore throughput to get a rough restore time. If a task holds 250 megabytes and the restore reads about 50 megabytes a second, the restore takes about five seconds. If the same task holds 25 gigabytes, the same throughput means about eight minutes. Measure your own throughput, since it depends on disk, network, and the embedded database.

What to watch as it grows

Live key count

A steadily rising key count with no plateau means entries are never removed. Investigate expiry before disks fill.

Ratio of log size to live data

If the changelog is many times larger than the store, compaction is not keeping up, and both disk use and restoration time suffer.

Disk headroom on brokers and instances

Local stores and changelog partitions both need room. Running low on either leads to slowdowns or failures that are harder to fix under pressure.

20A Worked Example With Real Numbers

Follow one small application through updates, compaction, a crash, and a restore.

The setting

A counting application tracks page views per user. Its input has three partitions, so it runs three tasks, each with one store and a matching changelog partition. Focus on task 1 and its changelog partition. The store uses the key user name and the value view count.

Phase one: updates

Input eventStore after updateChangelog record appendedChangelog offset
Asha viewsAsha 1Asha = 10
Bilal viewsAsha 1, Bilal 1Bilal = 11
Asha viewsAsha 2, Bilal 1Asha = 22
Chen viewsAsha 2, Bilal 1, Chen 1Chen = 13
Asha viewsAsha 3, Bilal 1, Chen 1Asha = 34
Bilal account deletedAsha 3, Chen 1Bilal = tombstone5

Phase two: compaction

The broker later compacts the closed segments of this partition. Offsets 0 and 2 are superseded by offset 4 for Asha, and offset 1 is superseded by the tombstone at offset 5 for Bilal. After compaction, the partition holds offset 3 for Chen, offset 4 for Asha, and offset 5, the tombstone for Bilal. Six records became three. Later, once the tombstone retention passes, offset 5 also disappears, leaving only the two live keys.

Phase three: the crash and restore

The instance running task 1 dies and its disk is lost. A different instance is assigned task 1. It has no checkpoint, so it restores from the start of the compacted changelog. It reads Chen = 1, applies it, reads Asha = 3, applies it, and reads the tombstone for Bilal, which removes Bilal from the store, though Bilal was never there in this fresh store. It reaches the end offset, and the store now shows Asha 3 and Chen 1, exactly matching the state before the crash.

sequenceDiagram
    participant Old as Old instance
    participant CL as Changelog partition 1
    participant New as New instance
    Old->>CL: Asha=1, Bilal=1, Asha=2, Chen=1, Asha=3, Bilal=tombstone
    Note over CL: Compaction keeps Chen=1, Asha=3, Bilal=tombstone
    Note over Old: Crash, disk lost
    New->>CL: Read from start
    CL-->>New: Chen=1
    CL-->>New: Asha=3
    CL-->>New: Bilal=tombstone
    Note over New: Store shows Asha 3 and Chen 1, matches the old state
    New->>New: Resume processing input
        
FIG 14 — Replaying a compacted changelog reproduces the exact state

What if compaction had not run yet?

The result would be identical, only slower. The new instance would read all six records, applying Asha = 1, then 2, then 3, and so on, and it would end in the same final state. Compaction changes how much work restoration takes, never what state it produces. This is the property that makes the changelog design robust: compaction can run late, run early, or run partially, and correctness is unaffected.

i
Lesson

The changelog stores results, not operations. Because of that, replaying any superset of the latest records for each key gives the correct state. Compaction is purely an efficiency measure.

21Performance and Restore-Time Trade-offs

Every choice around changelogs trades speed in normal operation against speed in recovery.

Costs in normal operation

Cost

Extra producer traffic

Each state update becomes a record sent to Kafka. Caching before the store softens this for hot keys.

Cost

Replication load on brokers

With three replicas, each changelog write is stored three times, adding disk and network work to the cluster.

Cost

Compaction work

The log cleaner reads and rewrites segments in the background, using brokers’ disk bandwidth and memory.

Cost

Standby replicas

Each standby reads the whole changelog stream and maintains a full copy, doubling the storage and read load for that task.

Levers to shorten recovery

1

Keep state small

Expire entries, use windows with modest retention, and avoid storing large values when a reference would do. Every byte in the store is a byte to restore.

2

Compact aggressively

Lower the dirty ratio and use smaller segments so the changelog stays close to the size of live data.

3

Use durable local storage

A persistent state directory turns most restarts into short replays from a checkpoint.

4

Add standby replicas

Warm copies turn failover into a brief catch-up rather than a full restore.

5

Spread state across more partitions

More partitions give smaller per-task state and more parallel restoration.

6

Tune the restore consumer

Larger fetch sizes and batch settings for the restore consumer raise restore throughput on large stores.

flowchart LR
    A["Large store, no standby, temporary disk"] --> B["Full restore after each failure"]
    C["Large store, persistent disk"] --> D["Short replay from checkpoint after clean restart"]
    E["Large store, standby replica"] --> F["Seconds of catch-up after failure"]
    G["Small store, compacted changelog"] --> H["Restore in seconds even from scratch"]
        
FIG 15 — Different designs give very different recovery times
Design choiceSteady-state costRecovery time
No standby, temporary diskLowestLongest, full restore
No standby, persistent diskLow, needs volume managementShort after clean restarts, long after crashes in exactly-once mode
One standby replicaAbout double storage and read load for stateSeconds
Two standby replicasAbout tripleSeconds, tolerates two failures with fast recovery
!
Do not confuse safety with speed

The changelog already makes state safe. Standby replicas and fast disks do not make data safer. They make recovery faster. Choose them based on your recovery time targets, not on a fear of data loss.

22Security and Privacy

Changelog topics contain the complete state of your application, including anything sensitive inside it.

Access control

A changelog topic contains every key and value stored in the state store, which may include personal data, account balances, or business secrets. Anyone who can read the topic can read the entire state. Access should therefore be limited to the application’s own identity. A convenient approach is to grant permissions by a prefix of the application ID, so that the application can create, read, write, and describe all internal topics whose names begin with its ID, and nobody else can.

Control 1

Prefixed permissions

Grant the application identity access to topics starting with its application ID. Deny everything else by default.

Control 2

Restrict human access

Engineers should not read changelog topics casually. Treat them like production databases.

Control 3

Encrypt in transit and at rest

Use TLS between applications and brokers, and encrypt disks on brokers and on instances holding local state.

Control 4

Protect the state directory

The local store is a full copy of the data. Set tight file permissions and secure storage volumes.

Control 5

Pre-create topics in locked-down clusters

If applications may not create topics, administrators create the changelogs with the exact names, partition counts, and settings the application expects.

Control 6

Guard against outside writers

Only the owning application should write to its changelogs. A rogue producer could inject records that corrupt restored state.

Deletion and the right to erasure

Privacy rules often require that data about a person can be deleted on request. In a changelog, deletion means emitting a tombstone for the key, and waiting for compaction and the tombstone retention period to remove the data physically. Until then, older values may still be present in the log. Data may also linger in the local stores of standby replicas, in aborted transactional records, in backups, and in downstream copies. A privacy-aware design documents this timeline, sets compaction and retention to meet the required deadlines, and, for very sensitive fields, encrypts values with per-person keys that can be destroyed to make the data unreadable at once.

!
A tombstone is a request, not an instant erase

Writing a tombstone does not immediately remove the earlier values from disk. Plan for the delay, verify it in tests, and involve your privacy or compliance team when defining the time limits.

23Monitoring, Logging, and Metrics

A healthy changelog pipeline is quiet. These signals show when something drifts.

SignalWhat it tells youHealthy pattern
Application stateRunning, rebalancing, or in errorMostly running, brief rebalancing during changes
Restoration progress and durationHow long tasks spend rebuilding stateShort and rare, longer only after large changes
Restore consumer lagDistance to the end of the changelog during a restoreFalls steadily to zero
Standby replica lagHow far warm copies trail the changelogSmall and stable
Changelog size versus store sizeEffectiveness of compactionLog is a small multiple of live data
Log cleaner activity on brokersWhether compaction keeps upSteady, no growing backlog of dirty segments
Producer errors on changelog writesTrouble writing state updatesNone
Task migration countHow often state moves between instancesRare, tied to deployments or failures
Store disk usage on instancesGrowth of local statePlateaus with the live key count

Hooks and tools

Hook

Restore listener

The library lets an application register a listener that is told when a restore starts, when each batch is restored, and when it ends. It is ideal for logging and for metrics on restore duration.

Hook

Store lag information

Applications can ask for the lag of their local stores compared with the changelog, which is useful for routing queries to the freshest copy.

Tool

Topic and consumer group tools

Describe the changelog topic, its partitions, and its settings, and check the lag of consumers reading it.

Tool

Log dump tool

Reads the changelog segment files directly, showing keys, values, timestamps, and tombstones, useful for investigating suspicious state.

Tool

Application reset tool

Resets an application’s input positions and deletes its internal topics when a full rebuild is intended. It must be used deliberately.

i
Log the restores

Record the start, duration, and record count of every restoration. Over months, a slow upward trend in restore time is an early warning of state growth, long before it becomes an outage.

flowchart TD
    A["Alert: input lag rising on one partition"] --> B{"Is the task restoring?"}
    B -->|"Yes"| C{"Is restore consumer lag falling?"}
    C -->|"Yes"| D["Wait, and consider standbys or smaller state"]
    C -->|"No"| E["Check changelog broker health, network, and restore consumer settings"]
    B -->|"No"| F{"Is the store disk full or slow?"}
    F -->|"Yes"| G["Free space, tune the embedded database, or add capacity"]
    F -->|"No"| H["Look at processing logic and downstream backpressure"]
        
FIG 16 — A triage path for lag on a stateful application

24Failure Scenarios and Recovery

Walk through what happens for each common failure, and what you should do.

FailureImmediate effectHow it resolves
Application instance crashesIts tasks are reassignedStandbys are promoted, or new owners restore from the changelog
Instance disk lostLocal state goneFull restore from the changelog on whichever instance takes over
Changelog partition leader failsBrief pause for writes and restores of that partitionNew leader elected among in-sync replicas, no acknowledged data lost
Broker outage below minimum in-sync replicasChangelog writes rejectedTasks stall or fail until brokers return, no wrong state is written
Changelog replication factor of one, broker lostData may be permanently lostState must be rebuilt by replaying input, possibly for a long time
Changelog topic deleted by mistakeRestoration cannot find its dataRecreate and rebuild from source topics after resetting the application
Changelog retention shortened by an administratorRecords removed that were still neededState restored with gaps, rebuild from input to correct

Rolling upgrade of the application

Instances restart one at a time. With a persistent state directory and clean shutdowns, each restarts with a short replay. With temporary storage, each restart triggers a restore, and standby replicas or warm-up replicas hide most of the delay.

Changelog offset out of range

If the changelog was truncated or recreated so that the checkpointed offset no longer exists, the library detects this, discards the local store, and restores from the beginning. It is safe but slow.

Restore that never seems to finish

Usually the store is far larger than expected, the changelog is barely compacted, or the restore consumer is throttled. Check the log size, compaction backlog, and fetch settings before anything else.

Full rebuild from input

When the changelog is lost or incompatible, the last resort is to reset the application and replay the input topics from the start. It is the slowest path, so it is worth protecting the changelog well to avoid it.

flowchart TD
    A["Task needs its state"] --> B{"Local store trusted and checkpoint valid?"}
    B -->|"Yes"| C["Replay only the tail of the changelog"]
    B -->|"No"| D{"Changelog intact and readable?"}
    D -->|"Yes"| E["Full restore from changelog"]
    D -->|"No"| F["Reset application, replay input topics to rebuild state"]
    C --> G["Resume processing"]
    E --> G
    F --> G
        
FIG 17 — The ladder of recovery paths, from fastest to slowest
i
Protect the middle rung

Most of the safety of a stateful application rests on the changelog being intact and well replicated. Guard it with sound replication settings, restricted access, and administrative discipline, so that the fast paths are always available.

25Design Patterns and Anti-patterns

Some designs around changelogs repeatedly work well. Others repeatedly fail, usually at three in the morning during a rebalance.

Patterns worth copying

Name every store explicitly

Give each state store, and each stateful operator, a deliberate and stable name. The changelog name is derived from it, so a stable name means the stored history survives every future edit of the topology.

Keep values small and keys well spread

Store compact summaries such as counters, totals and latest snapshots rather than whole event histories. Small values mean small changelogs, short restores, and light compaction work.

Standby replicas for latency-sensitive services

When a service must recover in seconds, keep one or more standby copies. They continuously replay the changelog, so a failover starts from almost-current state instead of an empty disk.

Persistent disks for state directories

Place the local state directory on storage that survives a restart of the application instance. A returning instance then only replays the tail of the changelog, guided by its checkpoint.

Treat the changelog as owned infrastructure

Restrict who can write or delete these topics, alert on their health, and back their configuration with the same review process as any production database.

Anti-patterns to avoid

ANTI-PATTERN-02 Avoid
Problem

A team lets the changelog topics be created with a single replica, or with a minimum in-sync setting of one, because it is quicker in a test environment, and the same settings reach production.

Why It’s Harmful

The changelog is the only durable copy of the state. Losing one broker loses that copy, and the only recovery is a slow full replay of the input topics, if the inputs even still exist.

Correct Approach

Use three replicas and a minimum of two in-sync replicas for every changelog in production, and verify the created topics rather than assuming the defaults applied.

ANTI-PATTERN-03 Avoid
Problem

A team stores an ever-growing list inside a single key, for example every order a customer has ever placed, and rewrites the whole list on each update.

Why It’s Harmful

Every update writes the full, growing value to the changelog. Storage, network use and restore time all grow with the size of the list, and one hot key can dominate a partition.

Correct Approach

Use a composite key such as customer plus order, so each record stays small and independent, or keep only an aggregate and send detailed history to a separate topic.

ANTI-PATTERN-04 Avoid
Problem

A team turns logging off for a store to save disk and network, and then relies on it for data that the business cannot recreate.

Why It’s Harmful

Without the changelog the store has no backup. Any machine loss, redeployment to a new disk, or rebalance to another instance starts that store empty, and results silently become wrong.

Correct Approach

Disable logging only for stores that are cheap to rebuild from a source topic or are truly disposable caches. Document the decision beside the store definition.

ANTI-PATTERN-05 Avoid
Problem

An operator edits the retention or cleanup policy of a changelog by hand to reclaim space, changing a compacted topic to a time-based deletion topic.

Why It’s Harmful

Time-based deletion removes old but still-current values, for example a customer who has not changed for a month. The next restore silently misses that state.

Correct Approach

Leave the cleanup policy alone. If space is the concern, reduce the number of keys, shrink the values, or expire keys deliberately by writing tombstones.

26Best Practices and Common Mistakes

Most changelog trouble comes from a small set of repeated mistakes. Knowing them ahead of time is cheaper than discovering them in an incident.

A working checklist

1
Design — Choose keys that spread evenly, keep values compact, and name every store and operator explicitly.
2
Configure — Set replication to three and minimum in-sync replicas to two for internal topics, and decide on standby replicas by your recovery time goal.
3
Provision — Size brokers for the changelog volume, and give instances persistent state directories with headroom for growth.
4
Secure — Limit write and delete access on internal topics to the application identity, and plan how erasure requests translate into tombstones.
5
Observe — Watch restore progress, changelog lag, compaction backlog, disk use and rebalance frequency, with alerts on each.
6
Rehearse — Kill instances, and a broker, in a test environment and time the recovery. Record the numbers so that planning uses facts.

Common mistakes and their fixes

MistakeWhat it causesFix
Relying on automatic store namesRenamed changelog after adding an operator; state appears lostName stores and operators explicitly
Ignoring restore time in capacity plansLong outages after a failure, in spite of good steady-state speedMeasure restore rate and plan standbys
Deleting local state directories casuallyFull restores for every task on the next startKeep directories on persistent storage
Changing partition counts of input topicsKeys move to different tasks; old state no longer lines upCreate a new application and rebuild
Skipping tombstones for expired keysStore and changelog grow without boundDelete keys deliberately when they end
Treating an internal topic as a public APIOther teams depend on a format that may changePublish a proper output topic instead
!
The invisible mistake

The most damaging errors are the ones that raise no alarm. Wrong retention, missing replicas and renamed stores all keep the application running. They only reveal themselves when a failure arrives and the promised recovery does not.

flowchart LR
    A["Design: keys, names, value size"] --> B["Configure: replicas, standbys"]
    B --> C["Provision: disks, brokers"]
    C --> D["Secure: ACLs, erasure plan"]
    D --> E["Observe: lag, restore, disk"]
    E --> F["Rehearse: kill and time recovery"]
    F --> A
        
FIG 18 — The continuous loop of building, running and proving a stateful application

The loop matters because none of these steps is a one-time task. Data volumes grow, topologies change, and infrastructure is replaced. Each change can quietly undo an earlier assumption, so the cycle of design, configuration, observation and rehearsal keeps repeating for as long as the application lives.

27Real-World and Industry Examples

The pattern of a fast local store backed by a compacted log appears wherever a stream of events must be turned into current facts.

Retail: live inventory

A retailer receives sale, return and delivery events from many stores. A stateful application keeps the current stock count per product and store. The changelog lets any instance take over that count instantly if a machine fails, while the count itself is served in microseconds during checkout.

Banking: fraud detection windows

A bank counts card transactions per customer over the last ten minutes. Windowed stores hold those counts, and windowed changelogs with time-based retention keep only the recent history. A failure of one instance does not reset any customer’s count, so a burst of suspicious activity is never forgotten.

Ride hailing: driver location and status

A platform keeps the latest position and availability of every driver. Each new update replaces the older one, which is the ideal shape for compaction. The changelog stays small even though billions of updates flow through it.

Streaming media: viewing sessions

A media service groups viewing events into sessions and computes watch time per user. Session stores and their changelogs hold the open sessions. When a session closes, tombstones and retention rules let the storage shrink back down.

Logistics: shipment state machines

A carrier tracks each parcel through a series of states. The store keeps the current state per parcel, and the changelog guarantees that a rebalance during a peak day never loses a parcel’s position in its journey.

1:1changelog partitions per task per store
3replicas recommended in production
0changelogs for global tables
1+standby copies for fast failover
A changelog is what turns a fast but fragile local table into something you can trust with a business.

What the examples share

In every case there is a stream of small updates, a need for very fast lookups of the latest answer, and a requirement that a machine failure must not change that answer. The combination of local storage for speed and a compacted log for durability fits each of these needs, which is why the same design appears across industries.

28Frequently Asked Questions

Short, direct answers to the questions that come up most often when people first meet changelog topics.

Q1Do I have to create changelog topics myself?

Usually not. The stream processing library creates them automatically when the application starts, provided its identity has permission to create topics. Some locked-down clusters require an administrator to create them ahead of time using the exact expected names.

Q2Why is my changelog compacted instead of time-limited?

Because it stores current state, not history. A value written a year ago is still the current value if nothing changed it. Deleting by age would erase valid state, so compaction keeps the latest record per key indefinitely.

Q3Is a changelog topic the same as the store’s data?

Equivalent, not identical. The changelog holds the history of changes, of which compaction keeps the newest per key. Replaying it produces the store’s contents, but the store itself is a separate, indexed local structure.

Q4Can I read a changelog topic from another application?

Technically yes, but it is discouraged. The internal format and naming are implementation details that may change. If others need the data, publish a purpose-built output topic.

Q5Does every state store have a changelog?

By default, yes, except stores for global tables and stores where logging was explicitly disabled. Some tables also reuse their source topic as the changelog when a specific optimization is enabled.

Q6What happens if I delete a changelog topic?

The application loses its backup. While it runs, the local stores still work, but the next restart or rebalance to a new instance finds nothing to restore. Rebuilding needs an application reset and a replay of the input topics.

Q7How long does a restore take?

It depends on the size of the changelog and the restore speed. Divide the data size by the sustained read and write rate of the instance. Measuring in a test is far more reliable than estimating.

Q8Why does processing stop while a store restores?

Processing on a task would otherwise use incomplete state and produce wrong results. The library waits until the store has caught up with the changelog before resuming that task.

Q9What does a standby replica cost?

Extra disk, network and processing for the copy of the store it maintains. In return, failover starts from nearly current state, cutting recovery from minutes or hours to seconds.

Q10Do standbys serve queries?

They can be used for reading stale data through interactive queries when enabled. They are not the primary, so their data may lag slightly behind.

Q11Can I change the number of partitions later?

Not safely for a running stateful application. Keys would map to different partitions, so existing state would sit in the wrong place. The safe route is a new application identity and a rebuild from the source.

Q12How do I remove data for one user permanently?

Write a tombstone for that key so the store and the changelog drop it. Remember that the tombstone itself is retained for a period before compaction removes it, and that copies in other topics need their own handling.

Q13Why is my changelog bigger than my store?

Compaction runs in the background, not instantly. Until it catches up, old versions of keys remain in the log. Tuning the cleaning thresholds trades storage against background work.

Q14Does exactly-once processing change how changelogs behave?

Yes. Changelog writes join the transaction, so an aborted attempt leaves no visible changes. After an unclean crash, the local store may contain uncommitted changes, so the library discards it and restores from the changelog.

Q15What is the difference between a standby and a warm-up replica?

A standby is a long-lived backup copy kept ready for failover. A warm-up replica is a temporary copy built on a new or different instance so that a task can move there without a long stall, after which it becomes the active copy.

Q16Does a renamed application lose its state?

Yes, in effect. The changelog names are built from the application identity, so a new identity looks for new, empty topics. Keep the identity stable for the life of the state.

Q17How do I know the changelog is healthy?

Check that it exists with the expected partition count and replication, that its consumer lag from the application is near zero during normal running, and that its size stays proportional to the number of keys.

Q18Can a changelog hold sensitive data safely?

It holds whatever the store holds, so it needs the same protection as any topic carrying that data: access control, encryption in transit, encryption at rest where required, and a plan for erasure.

Q19Is the local store required at all? Could I read the changelog directly?

Reading a log to answer a lookup means scanning it, which is slow. The local store provides an index for instant reads. The changelog is the durable record, and the store is the fast view of it.

Q20How can I test that recovery really works?

Run the application with realistic data, stop an instance abruptly, delete its local directory, and time how long until processing resumes and outputs match a known correct result. Repeat with a broker failure.

29Summary and Key Takeaways

A stateful stream application needs memory, and memory kept on one machine dies with that machine. Kafka’s answer is the changelog: every change to a local store is also written to a compacted, replicated topic, so the store is a fast view of a durable log. Compaction keeps the latest value per key and tombstones record deletions. Windowed stores add time-based retention. When a task moves or a machine returns, restoration replays the changelog, shortened by checkpoints and by standby and warm-up replicas. Exactly-once processing folds changelog writes into transactions, and stable names, sound replication, restricted access and careful monitoring keep the whole arrangement trustworthy over years of change.

Key Takeaways

  • The changelog is the backup of the state — every store update is also written to a topic, so a lost machine never means lost state.
  • Compaction fits the job — keeping the newest record per key bounds the size by the number of keys, not the number of updates, and tombstones record deletions.
  • Restoration is the price of failure — replay time is proportional to data size, so checkpoints, persistent disks and standby replicas exist to shrink it.
  • Exactly once includes state — changelog writes join the transaction, and an unclean crash means wiping the local store and restoring from committed data.
  • Names and partitions are contracts — changing an application identity, a store name, or the input partition count breaks the link between state and its history.
  • Configure it like a database — three replicas, two in-sync, restricted access, erasure plans, and alerts on lag, size and restore progress.
  • Prove it by rehearsal — kill instances and brokers on purpose, measure recovery, and keep the numbers in your runbook.