Home/Learn/Apache Kafka/Log Compaction

Log Compaction

Advanced
Administration

Compacted topics retain only the latest value per key, acting as a change-log materialised view; deleted records are signalled with a null-value tombstone.

Overview

Kafka's default retention policy is time-based or size-based: old log segments are deleted after retention.ms or when the log exceeds retention.bytes. Log compaction is an alternative retention policy that keeps at least the most recent value for every message key, indefinitely. This turns a Kafka topic into a change-log: consumers that join late can replay all keys from offset 0 and reconstruct the current state — not the full history, but the latest value per key. This is the foundation of Kafka Streams state stores, KTable semantics, and database CDC (Change Data Capture) pipelines. Deleted records are signalled by a tombstone — a record with the key and a null value — which compaction eventually removes.

How Log Compaction Works

The log cleaner is a background thread that periodically scans the "dirty" portion of a partition log (segments written since the last clean). For each key, it retains only the record with the highest offset and discards older duplicates. Compaction is not instantaneous — the log has a "head" (new, uncompacted records accepted as-is) and a "tail" (compacted, one record per key). Compaction guarantees: the latest value for every key is always available, but the history is not.

Key configs for compacted topics: - `cleanup.policy=compact` — enable log compaction - `min.cleanable.dirty.ratio` — fraction of log that must be dirty before cleaning triggers (default 0.5) - `min.compaction.lag.ms` — minimum time a record must remain in the head before being eligible for compaction (default 0)

Shell — Compacted Topic Setup
# Create a compacted topic
kafka-topics.sh --bootstrap-server localhost:9092 \
    --create \
    --topic user-profiles \
    --partitions 6 \
    --replication-factor 3 \
    --config cleanup.policy=compact \
    --config min.insync.replicas=2 \
    --config min.compaction.lag.ms=60000  # records survive at least 60s before compaction

# Compaction behaviour example for key "user-123":
# Offset 0: key=user-123, value={"name":"Alice","email":"a@ex.com"}
# Offset 5: key=user-123, value={"name":"Alice","email":"alice@ex.com"}  ← UPDATE
# Offset 9: key=user-123, value={"name":"Alice Smith","email":"alice@ex.com"} ← UPDATE
#
# After compaction — log retains only:
# key=user-123, value={"name":"Alice Smith","email":"alice@ex.com"} (offset 9)
# Offsets 0 and 5 are discarded.

# Convert an existing topic to compacted:
kafka-configs.sh --bootstrap-server localhost:9092 \
    --entity-type topics --entity-name user-profiles \
    --alter --add-config cleanup.policy=compact

Tombstones — Deleting Records from a Compacted Topic

To logically delete a key from a compacted topic, produce a tombstone: a record with the target key and a null value. The log cleaner will eventually remove both the tombstone and all older records for that key. Tombstones are retained for delete.retention.ms (default 24 hours) to give consumers time to see and process the deletion before it is purged.

Java — Tombstone for Key Deletion
// Producer — publish tombstone to delete a key
KafkaProducer<String, String> producer = new KafkaProducer<>(props);

// Tombstone: null value signals "delete this key"
ProducerRecord<String, String> tombstone =
    new ProducerRecord<>("user-profiles", "user-123", null);
producer.send(tombstone);

// After delete.retention.ms, the key "user-123" no longer exists in the topic
// Consumers who process the tombstone know to delete user-123 from their local store

// Spring Kafka — sending tombstone
@Service
public class UserProfileService {
    private final KafkaTemplate<String, UserProfile> kafka;

    public void deleteUser(String userId) {
        // KafkaTemplate with null value = tombstone
        kafka.send("user-profiles", userId, null);
    }

    public void updateUser(UserProfile profile) {
        kafka.send("user-profiles", profile.getId(), profile);
    }
}

Compaction Use Cases — KTable and CDC

Log compaction is the mechanism behind two powerful Kafka patterns:

**Kafka Streams KTable**: A KTable is a changelog stream represented as a compacted topic. Each record is an upsert (insert or update) for a key. KTable state stores are backed by compacted changelog topics so they can be rebuilt from scratch by replaying the compacted log.

**Change Data Capture (CDC)**: Debezium captures database row changes as Kafka records (key = PK, value = row state). Using a compacted topic means downstream consumers always have the latest row state without storing the entire history.

Java — KTable on Compacted Topic
// Kafka Streams KTable — backed by compacted changelog topic
StreamsBuilder builder = new StreamsBuilder();

// A KTable materialized view: latest user profile per user_id
KTable<String, UserProfile> userProfiles =
    builder.table(
        "user-profiles",   // compacted topic
        Materialized.as("user-profiles-store")
    );

// Join a stream with the KTable (enrich order events with user data)
KStream<String, Order> orders = builder.stream("orders");
KStream<String, EnrichedOrder> enriched = orders.join(
    userProfiles,
    (order, profile) -> new EnrichedOrder(order, profile.getEmail())
);
enriched.to("enriched-orders");

// On startup, Kafka Streams rehydrates the KTable store by replaying
// the compacted user-profiles topic from offset 0 — gets all current profiles
// without needing to replay years of full history.

Key Points to Remember

  • 1cleanup.policy=compact retains the latest value per key indefinitely; this is different from time/size-based retention which deletes old segments.
  • 2Compaction is asynchronous — the "head" of the log always contains all recent records; only the "tail" is compacted.
  • 3A tombstone (key + null value) signals deletion; it is retained for delete.retention.ms before being purged.
  • 4Log compaction is the backbone of Kafka Streams KTable state stores — it allows state to be rebuilt by replaying the compacted changelog.
  • 5Kafka Connect CDC pipelines (Debezium) use compacted topics so consumers can reconstruct current DB state by replaying from offset 0.
  • 6Compacted topics and time-based retention can be combined: cleanup.policy=compact,delete — compaction within the retention window, deletion after.

Interview Questions

Sign in to ask Aria
1

What is log compaction in Kafka and how does it differ from time-based retention?

MediumAmazon
2

What is a tombstone message and when would you produce one?

MediumUber
3

How does Kafka Streams use compacted topics for KTable state stores?

HardNetflix
4

A consumer reads a compacted topic from offset 0. Does it see all historical values for a key, or just the latest?

MediumLinkedIn
5

Can you combine log compaction with time-based retention? What does that mean?

HardGoogle

Ask Aria about Log Compaction

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…