Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Kafka Record Batch Format

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 ValueIntroducedNameKey Features
00.8.0Message v0Basic messages
10.10.0Message v1Timestamps
20.11.0Record BatchCompression, 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--> RecordsRecord batch layout: batch header and recordsRecord BatchBatch 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 0Record 1Record NCRC32-C covers:attributes throughend of batch
RecordBatch =>
baseOffset: INT64
batchLength: INT32
partitionLeaderEpoch: INT32
magic: INT8
crc: UINT32
attributes: INT16
lastOffsetDelta: INT32
baseTimestamp: INT64
maxTimestamp: INT64
producerId: INT64
producerEpoch: INT16
baseSequence: INT32
recordCount: INT32
records: [Record]
FieldTypeOffsetDescription
baseOffsetINT640Offset of first record in batch
batchLengthINT328Byte length from partitionLeaderEpoch to end
partitionLeaderEpochINT3212Leader epoch at produce time
magicINT816Format version (2 for current)
crcUINT3217CRC32-C of bytes 21 to end
attributesINT1621Batch flags
lastOffsetDeltaINT3223Offset delta of last record
baseTimestampINT6427Timestamp of first record
maxTimestampINT6435Maximum timestamp in batch
producerIdINT6443Producer ID for idempotence
producerEpochINT1651Producer epoch
baseSequenceINT3253Sequence number of first record
recordCountINT3257Number of records in batch
records[Record]61Variable-length record data

The 16-bit attributes field contains the following flags:

BitsMaskFieldDescription
0-20x07compressionCompression codec
30x08timestampType0=CreateTime, 1=LogAppendTime
40x10isTransactionalPart of transaction
50x20isControlBatchControl batch (commit/abort markers)
60x40hasDeleteHorizonMsDelete horizon present
7-15-unusedReserved for future use
ValueCodecDescription
0NoneNo compression
1GZIPGZIP compression
2SnappySnappy compression
3LZ4LZ4 compression
4ZSTDZstandard compression

Compression Scope

Compression applies to the records portion only, not the batch header. The header is always uncompressed.

ValueTypeDescription
0CreateTimeTimestamp set by producer
1LogAppendTimeTimestamp set by broker on append

Each record within a batch uses variable-length encoding:

Record =>
length: VARINT
attributes: INT8
timestampDelta: VARLONG
offsetDelta: VARINT
keyLength: VARINT
key: BYTES
valueLength: VARINT
value: BYTES
headersCount: VARINT
headers: [Header]
Field layout of a single recordField layout of a single recordRecordlength (varint)attributes (1B)timestampDelta (varlong)offsetDelta (varint)keyLength (varint)key (bytes)valueLength (varint)value (bytes)headersCount (varint)headers[]Length coversattributes throughend of headers
FieldTypeDescription
lengthVARINTByte length from attributes to end of headers
attributesINT8Reserved (must be 0)
timestampDeltaVARLONGTimestamp offset from batch baseTimestamp
offsetDeltaVARINTOffset relative to batch baseOffset
keyLengthVARINTKey byte length (-1 for null)
keyBYTESMessage key (omitted if keyLength < 0)
valueLengthVARINTValue byte length (-1 for null)
valueBYTESMessage value (omitted if valueLength < 0)
headersCountVARINTNumber of headers
headers[Header]Record headers
Header =>
keyLength: VARINT
key: BYTES
valueLength: VARINT
value: BYTES
FieldTypeDescription
keyLengthVARINTHeader key length
keyBYTESHeader key (UTF-8, not null-terminated)
valueLengthVARINTHeader value length (-1 for null)
valueBYTESHeader 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 offsetDeltaRecord offset derived from baseOffset and offsetDeltaRecord Batch (baseOffset=100)Record 0offsetDelta=0offset=100Record 1offsetDelta=1offset=101Record 2offsetDelta=2offset=102record_offset = baseOffset + offsetDeltalastOffsetDelta = 2
CalculationFormula
Record offsetbaseOffset + offsetDelta
Last offset in batchbaseOffset + lastOffsetDelta
Next expected offsetbaseOffset + lastOffsetDelta + 1
ScenarioOffset Behavior
Producer sendClient assigns relative offsets (0, 1, 2, ...)
Broker receiveBroker assigns absolute offsets, updates baseOffset
Fetch responseConsumer 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 handlingProducerBrokerConsumerProducerProducerBrokerBrokerConsumerConsumerCreateTime (default)Record(timestamp=T1)Preserves T1Record(timestamp=T1)LogAppendTimeRecord(timestamp=T1)Overwrites withbroker time T2Record(timestamp=T2)
SettingScopeOptions
log.message.timestamp.typeBrokerCreateTime, LogAppendTime
message.timestamp.typeTopicCreateTime, LogAppendTime

FieldPurposeAssignment
producerIdGlobally unique producer identifierBroker-assigned via InitProducerId
producerEpochFencing counter for same transactional.idIncrements on InitProducerId
baseSequenceFirst sequence number in batchProducer-assigned, monotonic
Idempotent producer sequence numbers and gap detectionProducerBrokerProducer(PID=100)Producer(PID=100)BrokerBrokerBatch(seq=0, count=3)Records 0, 1, 2AckBatch(seq=3, count=2)Records 3, 4AckBatch(seq=3, count=2)Retry (duplicate)Ack (idempotent, no duplicate)Batch(seq=10, count=1)Gap detectedOutOfOrderSequenceException
RequirementLevel
Sequence numbers must be monotonically increasing per partitionmust
Sequence number gaps result in OutOfOrderSequenceExceptionmust
Duplicate sequence numbers are silently deduplicatedmust
Sequence numbers wrap at INT32_MAXmay

For non-idempotent producers:

FieldValue
producerId-1
producerEpoch-1
baseSequence-1

Control batches contain transaction markers rather than user data.

AttributeValue
isControlBatchtrue (bit 5 set)
recordCount1 (single control record)
TypeValueDescription
ABORT0Transaction aborted
COMMIT1Transaction committed
ControlRecord =>
version: INT16
type: INT16
FieldTypeDescription
versionINT16Control record version (currently 0)
typeINT16Control type (0=ABORT, 1=COMMIT)
Transactional data batches followed by a commit control batchTransactional data batches followed by a commit control batchTransaction with Control BatchesData Batch 1isTransactional=truePID=100Data Batch 2isTransactional=truePID=100Control BatchisControlBatch=truetype=COMMITControl batch marksend of transactionsame transactioncommit marker

Compression applied to the records portion onlyCompression applied to the records portion onlyUncompressed BatchCompressed BatchHeader (61 bytes)Record 0Record 1Record NHeader (61 bytes)Compressed RecordsOnly records are compressedHeader remains uncompressedcopycompress
AspectSpecification
ScopeRecords portion only (after header)
Inner batchesNested compression not supported
Empty batchesMay be compressed (compresses to minimal size)
CRC calculationOver compressed data
SettingScopeOptions
compression.typeProducernone, gzip, snappy, lz4, zstd
compression.typeBrokerproducer, none, gzip, snappy, lz4, zstd
compression.typeTopicproducer, none, gzip, snappy, lz4, zstd

Broker Compression Behavior:

Broker SettingProducer CodecStored Codec
producerAnySame as producer
noneAnyUncompressed
Specific codecAnySpecified codec

Re-compression Cost

Setting broker compression different from producer compression causes re-compression on every message, significantly increasing broker CPU usage.


PropertyValue
AlgorithmCRC32-C (Castagnoli)
Polynomial0x1EDC6F41
CRC32-C coverage within a record batchCRC32-C coverage within a record batchRecord BatchCRC Covered RegionbaseOffset (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 bytes21 through endof batch
RequirementLevel
Producer must compute CRC before sendingmust
Broker must validate CRC on receivemust
Consumer must validate CRC on fetchshould
CRC mismatch must result in CorruptRecordExceptionmust

LimitDefaultConfiguration
Maximum record size1 MBmessage.max.bytes (broker), max.message.bytes (topic)
Maximum batch size1 MBmax.partition.fetch.bytes (consumer fetch limit)
Maximum request size100 MBsocket.request.max.bytes (broker)
Size ComponentCalculation
Record sizelength field value + length field size
Batch sizebatchLength + 12 (header prefix)
Batch overhead61 bytes minimum (empty batch)

MessageV0 =>
crc: INT32
magic: INT8 (= 0)
attributes: INT8
key: BYTES
value: BYTES
FeatureSupport
Compression✅
Timestamps❌
Headers❌
Transactions❌
MessageV1 =>
crc: INT32
magic: INT8 (= 1)
attributes: INT8
timestamp: INT64
key: BYTES
value: BYTES
FeatureSupport
Compression✅
Timestamps✅
Headers❌
Transactions❌
DirectionBehavior
v0/v1 → v2Broker converts on log compaction or fetch
v2 → v0/v1Broker 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.


RequirementLevel
Must use magic byte 2 for Kafka 0.11+must
Must compute CRC32-C correctlymust
Must assign monotonic sequence numbers (if idempotent)must
Should batch multiple records for efficiencyshould
Should compress records for bandwidth efficiencyshould
RequirementLevel
Must validate CRC on fetchshould
Must handle compression transparentlymust
Must filter aborted transaction records (if read_committed)must
Must handle control batches appropriatelymust
RequirementLevel
Must validate CRC on producemust
Must assign monotonic offsetsmust
Must preserve record ordering within batchmust
Must support all compression codecsmust
May re-compress with different codecmay