Skip to content

Commit offsets yourself

Consumer groups · Article 8 of 12 · 4 min read

Committing at the right moment is the difference between losing an order and handling it twice.

The short version

A commit saves the position of a group in a partition. Turn off automatic commits and call commit after the work is done.

Details

Automatic commits run on a timer, so a crash can skip messages that were committed but never handled. Committing after the work means a crash can only cause a repeat. This is called at least once delivery, so make your handling safe to repeat.

Change the config of your consumer:

consumer = Consumer({
    "bootstrap.servers": "localhost:9092",
    "group.id": "billing",
    "auto.offset.reset": "earliest",
    "enable.auto.commit": False,
})

Then commit after each message is handled:

        print(msg.key().decode(), msg.value().decode(), msg.offset())
        consumer.commit(message=msg, asynchronous=False)

Try it yourself

Make both changes in consume.py, run it, then stop it with Ctrl+C and describe the group:

docker exec kafka /opt/kafka/bin/kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 --describe --group billing

You should see LAG at 0 on every partition, because each message was committed after it was printed.

Explain it to the duck

Why is a repeat safer than a skip, and what does your code need to do to handle a repeat?

Tested with: Kafka 4.1.0 in Docker, Python 3.12 and confluent-kafka 2.11.