Apache Kafka wire format varint values are doubled
When I look at the Kafka Protocol Guide, and inside that specifically at the Record format, it says that many number fields are encoded as varint. To explain varint, a link is given to Protobuf documentation. It’s a variable-length encoding; fair enough.
When I try to read the binary format of an Apache Kafka segment file (something akin to 0000000123.log), I am able to match the documented structures of the always fixed-length RecordBatch and the variable Records. However, the varint encoding does not seem to match the documentation. The resulting decoded value is always exactly double the actual number. Does anyone know, why that is?
Let’s make an example with a real file from my Apache Kafka 3.6 cluster.
On the left is the hexdump, and on the right I will annotate the meaning.
hexdump -C /var/lib/kafka/data-1/kafka-log0/perf-testing-0/00000000000000000007.log
[...]
00000290 76 61 6c 75 65 23 76 61 6c 75 65 23 31 31 00 00 |value#value#11..| (rest of previous batch, 15 bytes), New RecordBatch
000002a0 00 00 00 00 00 00 0d 00 00 01 83 00 00 03 6d 02 |..............m.| FirstOffset(..7 bytes), Length of batch(4 bytes), PartitionLeaderEpoch(4 bytes), Magic Byte/Message format v2 (1 byte)
000002b0 26 4b 85 d7 00 00 00 00 00 01 00 00 01 93 4d d0 |&K............M.| CRC(4 bytes), Attributes(2 bytes), LastOffsetDelta(4 bytes), FirstTimestamp(6 bytes..)
000002c0 5d c6 00 00 01 93 4d d0 5d c7 00 00 00 00 00 08 |].....M.].......| FirstTimestamp(..2 bytes), MaxTimestamp(8 bytes), ProducerId(6 bytes..)
000002d0 02 cc 00 00 00 00 00 01 00 00 00 02 cc 02 00 00 |................| ProducerId(..2 bytes), ProducerEpoch(2 bytes), FirstSequence(4 bytes), NumberOfRecords(4 bytes)=2 Record: Length(varint 2 bytes) cc 02, Attributes(1 byte)x, TimestampDelta(varlong 1 byte)
000002e0 00 30 6b 65 79 23 6b 65 79 23 6b 65 79 23 6b 65 |.0key#key#key#ke| OffsetDelta(varint 1 byte), KeyLength(varint 1 byte) 30, Key content (14 bytes..)
000002f0 79 23 6b 65 79 23 2d 31 31 31 8e 02 76 61 6c 75 |y#key#-111..valu| Key content (..10 bytes), ValueLength (varint 2 bytes) 8e 02, Value(remaining bytes from here on)
00000300 65 23 76 61 6c 75 65 23 76 61 6c 75 65 23 76 61 |e#value#value#va| Value content (16 bytes)
00000310 6c 75 65 23 76 61 6c 75 65 23 76 61 6c 75 65 23 |lue#value#value#| Value content (16 bytes)
[...]
In this snippet I have several varint length values, and two simple ones decode as follows:
Key length: 0x30 = 0|0110000 is equal to 110000 (bin) = 48 (dec) = 2 * 24
Value length 0x8e 0x02 = 1|0001110 0|0000010 combine to 100001110 (bin) = 270 (dec) = 2 * 135
When I manually count the number of bytes in the Key though (easy because it’s short) I see that the key is 24 bytes long. The decoded varint claims a length of 48.
Now let’s compare these values to the output of the kafka-dump-log.sh tool, which also confirms my observations:
bash /opt/kafka/bin/kafka-dump-log.sh --deep-iteration --print-data-log --max-bytes 1800 --files /var/lib/kafka/data-1/kafka-log0/perf-testing-0/00000000000000000007.log
[...]
baseOffset: 13 lastOffset: 14 count: 2 baseSequence: 1 lastSequence: 2 producerId: 525004 producerEpoch: 0 partitionLeaderEpoch: 877 isTransactional: false isControl: false deleteHorizonMs: OptionalLong.empty position: 671 CreateTime: 1732177321415 size: 399 magic: 2 compresscodec: none crc: 642483671 isvalid: true
| offset: 13 CreateTime: 1732177321414 keySize: 24 valueSize: 135 sequence: 1 headerKeys: []
[...]
It says it right there: keySize: 24 valueSize: 135. These sizes are only half of what the varints say.
Read more here: Source link
