This document specifies the record batch format used in Apache Kafka for storing and transmitting messages. The record batch is the fundamental unit of data in Kafka, used in produce requests, fetch responses, and log storage.
Magic Value Introduced Name Key Features 0 0.8.0 Message v0 Basic messages 1 0.10.0 Message v1 Timestamps 2 0.11.0 Record Batch Compression, headers, transactions
Current Format
This document focuses on Magic 2 (Record Batch format), the current format used since Kafka 0.11.0. Legacy formats (Magic 0 and 1) are deprecated but may still be encountered in older clusters.
Record batch layout: batch header and records Record batch layout: batch header and records Record Batch Batch Header (61 bytes) Records (compressed) baseOffset (8B) batchLength (4B) partitionLeaderEpoch (4B) magic (1B) crc (4B) attributes (2B) lastOffsetDelta (4B) baseTimestamp (8B) maxTimestamp (8B) producerId (8B) producerEpoch (2B) baseSequence (4B) recordCount (4B) Record 0 Record 1 Record N CRC32-C covers: attributes through end of batch
partitionLeaderEpoch: INT32
Field Type Offset Description baseOffsetINT64 0 Offset of first record in batch batchLengthINT32 8 Byte length from partitionLeaderEpoch to end partitionLeaderEpochINT32 12 Leader epoch at produce time magicINT8 16 Format version (2 for current) crcUINT32 17 CRC32-C of bytes 21 to end attributesINT16 21 Batch flags lastOffsetDeltaINT32 23 Offset delta of last record baseTimestampINT64 27 Timestamp of first record maxTimestampINT64 35 Maximum timestamp in batch producerIdINT64 43 Producer ID for idempotence producerEpochINT16 51 Producer epoch baseSequenceINT32 53 Sequence number of first record recordCountINT32 57 Number of records in batch records[Record] 61 Variable-length record data
The 16-bit attributes field contains the following flags:
Bits Mask Field Description 0-2 0x07 compression Compression codec 3 0x08 timestampType 0=CreateTime, 1=LogAppendTime 4 0x10 isTransactional Part of transaction 5 0x20 isControlBatch Control batch (commit/abort markers) 6 0x40 hasDeleteHorizonMs Delete horizon present 7-15 - unused Reserved for future use
Value Codec Description 0 None No compression 1 GZIP GZIP compression 2 Snappy Snappy compression 3 LZ4 LZ4 compression 4 ZSTD Zstandard compression
Compression Scope
Compression applies to the records portion only, not the batch header. The header is always uncompressed.
Value Type Description 0 CreateTime Timestamp set by producer 1 LogAppendTime Timestamp set by broker on append
Each record within a batch uses variable-length encoding:
Field layout of a single record Field layout of a single record Record length (varint) attributes (1B) timestampDelta (varlong) offsetDelta (varint) keyLength (varint) key (bytes) valueLength (varint) value (bytes) headersCount (varint) headers[] Length covers attributes through end of headers
Field Type Description lengthVARINT Byte length from attributes to end of headers attributesINT8 Reserved (must be 0) timestampDeltaVARLONG Timestamp offset from batch baseTimestamp offsetDeltaVARINT Offset relative to batch baseOffset keyLengthVARINT Key byte length (-1 for null) keyBYTES Message key (omitted if keyLength < 0) valueLengthVARINT Value byte length (-1 for null) valueBYTES Message value (omitted if valueLength < 0) headersCountVARINT Number of headers headers[Header] Record headers
Field Type Description keyLengthVARINT Header key length keyBYTES Header key (UTF-8, not null-terminated) valueLengthVARINT Header value length (-1 for null) valueBYTES Header value (omitted if valueLength < 0)
Header Keys
Header keys must be valid UTF-8 strings. Header keys should not contain null characters. Duplicate header keys are permitted.
Record offset derived from baseOffset and offsetDelta Record offset derived from baseOffset and offsetDelta Record Batch (baseOffset=100) Record 0 offsetDelta=0 offset=100 Record 1 offsetDelta=1 offset=101 Record 2 offsetDelta=2 offset=102 record_offset = baseOffset + offsetDelta lastOffsetDelta = 2
Calculation Formula Record offset baseOffset + offsetDeltaLast offset in batch baseOffset + lastOffsetDeltaNext expected offset baseOffset + lastOffsetDelta + 1
Scenario Offset Behavior Producer send Client assigns relative offsets (0, 1, 2, ...) Broker receive Broker assigns absolute offsets, updates baseOffset Fetch response Consumer receives absolute offsets
Offset Gaps
Compaction and transactional aborts may create offset gaps. Consumers must not assume consecutive offsets.
record_timestamp = baseTimestamp + timestampDelta
CreateTime and LogAppendTime timestamp handling Producer Broker Consumer Producer Producer Broker Broker Consumer Consumer CreateTime (default) Record(timestamp=T1) Preserves T1 Record(timestamp=T1) LogAppendTime Record(timestamp=T1) Overwrites with broker time T2 Record(timestamp=T2)
Setting Scope Options log.message.timestamp.typeBroker CreateTime, LogAppendTime message.timestamp.typeTopic CreateTime, LogAppendTime
Field Purpose Assignment producerIdGlobally unique producer identifier Broker-assigned via InitProducerId producerEpochFencing counter for same transactional.id Increments on InitProducerId baseSequenceFirst sequence number in batch Producer-assigned, monotonic
Idempotent producer sequence numbers and gap detection Producer Broker Producer (PID=100) Producer (PID=100) Broker Broker Batch(seq=0, count=3) Records 0, 1, 2 Ack Batch(seq=3, count=2) Records 3, 4 Ack Batch(seq=3, count=2) Retry (duplicate) Ack (idempotent, no duplicate) Batch(seq=10, count=1) Gap detected OutOfOrderSequenceException
Requirement Level Sequence numbers must be monotonically increasing per partition must Sequence number gaps result in OutOfOrderSequenceException must Duplicate sequence numbers are silently deduplicated must Sequence numbers wrap at INT32_MAX may
For non-idempotent producers:
Field Value producerId-1 producerEpoch-1 baseSequence-1
Control batches contain transaction markers rather than user data.
Attribute Value isControlBatchtrue (bit 5 set) recordCount1 (single control record)
Type Value Description ABORT 0 Transaction aborted COMMIT 1 Transaction committed
Field Type Description versionINT16 Control record version (currently 0) typeINT16 Control type (0=ABORT, 1=COMMIT)
Transactional data batches followed by a commit control batch Transactional data batches followed by a commit control batch Transaction with Control Batches Data Batch 1 isTransactional=true PID=100 Data Batch 2 isTransactional=true PID=100 Control Batch isControlBatch=true type=COMMIT Control batch marks end of transaction same transaction commit marker
Compression applied to the records portion only Compression applied to the records portion only Uncompressed Batch Compressed Batch Header (61 bytes) Record 0 Record 1 Record N Header (61 bytes) Compressed Records Only records are compressed Header remains uncompressed copy compress
Aspect Specification Scope Records portion only (after header) Inner batches Nested compression not supported Empty batches May be compressed (compresses to minimal size) CRC calculation Over compressed data
Setting Scope Options compression.typeProducer none, gzip, snappy, lz4, zstd compression.typeBroker producer, none, gzip, snappy, lz4, zstd compression.typeTopic producer, none, gzip, snappy, lz4, zstd
Broker Compression Behavior:
Broker Setting Producer Codec Stored Codec producerAny Same as producer noneAny Uncompressed Specific codec Any Specified codec
Re-compression Cost
Setting broker compression different from producer compression causes re-compression on every message, significantly increasing broker CPU usage.
Property Value Algorithm CRC32-C (Castagnoli) Polynomial 0x1EDC6F41
CRC32-C coverage within a record batch CRC32-C coverage within a record batch Record Batch CRC Covered Region baseOffset (8B) batchLength (4B) partitionLeaderEpoch (4B) magic (1B) crc (4B) attributes (2B) lastOffsetDelta (4B) baseTimestamp (8B) maxTimestamp (8B) producerId (8B) producerEpoch (2B) baseSequence (4B) recordCount (4B) records (variable) CRC32-C of bytes 21 through end of batch
Requirement Level Producer must compute CRC before sending must Broker must validate CRC on receive must Consumer must validate CRC on fetch should CRC mismatch must result in CorruptRecordException must
Limit Default Configuration Maximum record size 1 MB message.max.bytes (broker), max.message.bytes (topic)Maximum batch size 1 MB max.partition.fetch.bytes (consumer fetch limit)Maximum request size 100 MB socket.request.max.bytes (broker)
Size Component Calculation Record size length field value + length field sizeBatch size batchLength + 12 (header prefix)Batch overhead 61 bytes minimum (empty batch)
Feature Support Compression ✅ Timestamps ❌ Headers ❌ Transactions ❌
Feature Support Compression ✅ Timestamps ✅ Headers ❌ Transactions ❌
Direction Behavior v0/v1 → v2 Broker converts on log compaction or fetch v2 → v0/v1 Broker down-converts for old consumers
Down-Conversion Overhead
Down-conversion from v2 to v0/v1 is CPU-intensive and loses features (headers, exactly-once semantics). Upgrade all clients to avoid down-conversion.
Requirement Level Must use magic byte 2 for Kafka 0.11+ must Must compute CRC32-C correctly must Must assign monotonic sequence numbers (if idempotent) must Should batch multiple records for efficiency should Should compress records for bandwidth efficiency should
Requirement Level Must validate CRC on fetch should Must handle compression transparently must Must filter aborted transaction records (if read_committed) must Must handle control batches appropriately must
Requirement Level Must validate CRC on produce must Must assign monotonic offsets must Must preserve record ordering within batch must Must support all compression codecs must May re-compress with different codec may