"Blog" File Format Specification

A unified file format for WAL, Blob, and related data in RocksDB

Status: Implementation in progress  |  Format name: "blog" (portmanteau of "blob" + "log")

Vocabulary

TermScopeDefinition
RecordBlog fileA top-level unit in the blog file body: blob record, WriteBatch record, preamble-start record, footer record.
Operation (op)WriteBatchAn entry within a WriteBatch: Put op, Delete op, Merge op, etc.
Compact formatRecordA record with varint length ≤ 3 bytes. Omits the type byte (type from header).
Full formatRecordA record with varint length > 3 bytes. Includes explicit type, pre-payload compression type, and prefix checksum.

File-Level Structure

A blog file consists of three regions: a fixed header with a property section, a body of records, and an optional footer composed of footer records.

+---------------------------------------------------------------------+ | FILE HEADER (40 bytes fixed) | | 96-bit magic, schema version, checksum type, compact record type, | | flags, escape sequence, reserved, property section size, checksum | +---------------------------------------------------------------------+ | HEADER PROPERTY SECTION | | Named key-value pairs + 5-byte trailer (compression + checksum) | | Followed by arbitrarily long padding (0x00 or 0xFF) | +---------------------------------------------------------------------+ | FILE BODY | | Sequence of records, each starting with the escape sequence. | | Record types: blob, WriteBatch, preamble-start, footer records. | +---------------------------------------------------------------------+ | FILE FOOTER (optional) | | Footer index records (0xFD), footer properties record (0xFE), | | footer locator record (0xFF) -- must be last. | +---------------------------------------------------------------------+

Fixed Header (40 bytes)

OffsetSizeField
012File magic: kBlogFileMagic -- opaque 12 bytes ("Blog" ASCII + XXH3_64bits("Blog"))
121Schema version (currently 0). Newer versions are rejected by older readers.
131Checksum type (ChecksumType enum: 0=none, 1=CRC32c, 4=XXH3). Configured via DBOptions::blog_checksum.
141Compact record type (BlogRecordType -- type for all compact-format records)
151Flags: bit 0 = kBlogFileRecycled (file may contain trailing stale data)
1610Escape sequence: bytes [0-5] random (byte[0] not 0x00/0xFF), bytes [6-9] = lower32(XXH3_64bits_withSeed(bytes[0-5], kBlogEscapeSeqSeed)). First 4 bytes also serve as incarnation ID (little-endian via DecodeFixed32).
266Reserved (zero)
324Property section size (uint32 LE) -- includes 5-byte trailer
364Fixed header checksum -- covers bytes [0, 36)

Header Property Section

Immediately follows the fixed header. Contains named key-value pairs encoded as:

[name_len: varint32] [name: name_len bytes] [value_len: varint32] [value: value_len bytes] ...

Followed by a 5-byte trailer: [compression_type: 1B][checksum: 4B] (same as block-based table). The checksum covers the property data + compression_type, with a context checksum modifier using the incarnation ID at offset kBlogFileFixedHeaderSize.

After the property section, arbitrarily long padding (0x00 or 0xFF, differing from the last meaningful byte) aligns the first record's escape sequence. This supports stronger alignment requirements (flush/sync boundaries) or recycling files of all zeros.

Property Name Convention

First letterMeaning
Uppercase (e.g. "CompressionCompatibilityName")Required -- reader must recognize or reject the file
Lowercase (e.g. "role")Ignorable -- reader may skip unrecognized properties

Known Header Properties

NameRequired?Description
"CompressionCompatibilityName"Required (if any compression)Compression manager compatibility name (e.g. "builtin_v2")
"WriteBatchStreamingCompressionType"Required (if streaming active)2-digit hex of CompressionType for streaming WriteBatch compression (e.g. "07" for ZSTD). Presence means WriteBatch records with kStreamingCompressionSentinel (0x7F) trailer are part of a cross-record streaming context.
"TimestampSize"Required (if >0)Size of user timestamps
"PredecessorWalInfo"Required (if tracking)Predecessor WAL metadata for verification
"role"IgnorableFile purpose hint: "wal", "blob", "rlog"
"compressionSettings"IgnorableHuman-readable compression settings for debugging
"creationTime"IgnorableUnix timestamp of file creation
"creator"IgnorableHost/process identifier for debugging

Escape Sequences

Decision: 10-byte escape sequence: 6 random bytes + 4 derived bytes. First byte not 0x00 nor 0xFF (distinguishable from padding). Bytes [6-9] = lower32(XXH3_64bits_withSeed(bytes[0-5], kBlogEscapeSeqSeed)), enabling heuristic detection without knowing the header.

Escape sequences must be 32-bit aligned in the file. The first 4 bytes (little-endian) serve as the incarnation ID for context checksums.

Primary Roles

Record Types and Formats

Record Type Values

ValueNameUsage
Body record types (0x01-0x7F)
0x01kBlogBlobRecordBlob data (typically a separated value)
0x02kBlogPreambleStartRecordPreamble marker (stub)
0x03kBlogWriteBatchRecordSerialized WriteBatch
0x04-0x7FReserved for future body record types
Footer record types (0xF0-0xFF, in expected order of occurrence with gaps)
0xF0kBlogFooterIndexRecordLarge footer metadata (sparse index)
0xF1-0xF3Reserved for future footer data records
0xF4-0xF7Reserved for future footer data records
0xF8kBlogFooterPropertiesRecordSmall footer metadata (named properties)
0xF9-0xFBReserved
0xFCkBlogFooterFileChecksumInfoFull file checksum state dump. Allows inferring the file checksum from just reading the tail. Placeholder for future use.
0xFD-0xFEReserved
0xFFkBlogFooterLocatorRecordFooter locator with relative offsets. Must be the last record in the file.

Length Semantics

Compact Record Format (varint length ≤ 2 bytes, default record type)

[escape_seq: 10B] [varint length: 1-2B] [payload: length bytes] [compression_type: 1B] [checksum: 4B] [padding: 0+ B]

No type byte -- type comes from the header's compact_record_type field. Used for payloads < 16 KiB.

Full Record Format (varint length > 2 bytes, or forced)

[escape_seq: 10B] [varint length: 3+B] [type: 1B] [compression_type: 1B] [prefix_checksum: 4B] [payload: length bytes] (if length > 0) [compression_type: 1B] [checksum: 4B] (if length > 0) [padding: 0+ B]

The prefix_checksum covers (varint + type + compression_type), with a context checksum modifier for defense-in-depth. Validates the length before attempting a potentially large read. The pre-payload compression_type enables streaming decompression without seeking to the trailer.

Records that don't match the compact_record_type (e.g. footer records, preamble-start) always use full format by encoding the varint with ≥ 3 bytes (irregular encoding).

All checksums in blog files (fixed header, property section trailer, record prefix, record trailer) use context checksum modifiers incorporating the incarnation ID, providing defense-in-depth for misplaced data detection even with kNoChecksum.

Compression

Per-Record Compression

Each record's trailer compression_type byte indicates the compression algorithm used for that record's payload. The actual type comes from Compressor::CompressBlock output, not the configured type. This is the same approach used by SST block-based table builders.

Streaming Compression (WriteBatch records only)

When the header property WriteBatchStreamingCompressionType is set, WriteBatch record payloads may use streaming compression across records. Such records use kStreamingCompressionSentinel (0x7F) as their trailer's compression_type byte, indicating "compressed using the streaming algorithm declared in the header."

A record with kNoCompression (0x00) in the trailer remains individually uncompressed even when streaming is active, enabling dynamic on/off switching based on compression effectiveness.

Non-WriteBatch records (blob records, footer records) are always outside the streaming context and use per-record compression, keeping blob data randomly readable.

Currently only ZSTD is supported for streaming compression. The WriteBatchStreamingCompressionType property is required (uppercase first letter), so older readers that don't understand it will reject the file rather than misinterpret streaming-compressed payloads.

Padding Scheme

Padding fills the gap between a record's trailer (or the header property section) and the next escape sequence. The padding byte value is chosen to differ from the last meaningful byte, enabling unambiguous scanning.

Last meaningful bytePad withMandatory?
0x000xFFYes -- at least 1 byte required
0xFF0x00Yes -- at least 1 byte required
Anything else0x00 (writer's choice)Only if needed for alignment

Padding may be arbitrarily long for stronger alignment requirements (e.g. flush/sync boundaries) or for recycling a file of all zeros. The reader skips padding by scanning forward over 0x00/0xFF bytes in 4-byte-aligned chunks until finding a byte that is neither, which is the first byte of the next escape sequence (guaranteed to be neither 0x00 nor 0xFF).

Context Checksums

Decision (adopted from SST format_version=6): Each record's checksum incorporates a 32-bit incarnation ID (first 4 bytes of the escape sequence). Uses ChecksumModifierForContext(incarnation_id, record_offset) -- the same mechanism as block-based SST files.

Context checksums provide defense-in-depth for recycling detection and general data integrity verification with zero per-record overhead.

The checksum type is configured from the CF's BlockBasedTableOptions::checksum setting (for blob files) or the default CF's setting (for WAL files). This is a reasonable but hopefully temporary way of configuring blog file internal checksums; a dedicated option may be added later.

Footer

The footer is composed of regular records using the full record format, maintaining simple forward parsability. A file is cleanly sealed when a valid footer locator record is the last record. Footer records appear in the order listed below.

  1. Footer index records (0xF0): Large metadata like sparse indexes for range queries. Zero or more per file.
  2. Footer properties record (0xF8): Named properties (same encoding as header properties, but the full record format's own trailer provides integrity). At most one per file.
  3. Footer file checksum info (0xFC): Placeholder for future use. Would contain a dump of the full file checksum state, allowing the file checksum to be inferred from just reading the tail of the file.
  4. Footer locator record (0xFF): Must be the last record. Contains relative offsets (in 4-byte units) to other footer records, sequential relative to each other in reverse order.

Footer Locator Payload

[record_type: 1B] [relative_offset_in_4B_units: uint32 LE] ...

First entry: distance from locator's escape_seq backward to the preceding footer record. Subsequent: from the previous entry backward to the next.

Known Footer Properties

NameDescription
"blobCount"Number of blob records
"totalBlobBytes"Total uncompressed blob payload bytes
"totalBlobBytesCompressed"Total compressed blob payload bytes on disk
"totalWriteBatchBytes"Total uncompressed WriteBatch payload bytes
"totalWriteBatchBytesCompressed"Total compressed WriteBatch payload bytes on disk
"minSequence"Minimum sequence number
"maxSequence"Maximum sequence number

WAL Recovery Modes

The escape sequence is the primary mechanism for detecting WAL recycling and for non-linear data recovery. The kBlogFileRecycled flag in the header indicates the file may contain trailing stale data from a previous incarnation.

Mode Behavior on Undecodable Data Escape Sequence Role
kAbsoluteConsistency Error on any failure, including incomplete trailing records. If recycled and no escape sequence found in trailing data, accept as clean end. Distinguishes stale recycled data from corruption.
kTolerateCorruptedTailRecords Incomplete trailing record (Incomplete status): tolerate, stop at last complete record. Corruption: scan for escape sequence. Not found = clean end (recycled or preallocated). Found with valid record beyond = mid-file corruption (error). Distinguishes tail damage from mid-file corruption.
kPointInTimeRecovery (default) Stop at first inconsistency. Incomplete trailing records tolerated. Recycled trailing data accepted. Diagnostic improvement.
kSkipAnyCorruptedRecords On corruption, scan forward for next escape sequence. If found, try to decode and verify; resume if valid. If not found, stop. Essential for record discovery after corruption.

WriteBatch Blob Preamble

A blob preamble is a sequence of blob records preceded by a preamble-start record (type 0x02, full format, length=0) and followed by the associated WriteBatch record. The blob records contain separated values referenced by file_number = 0 blob references in the WriteBatch.

This functionality is stubbed in the current implementation: the preamble-start record type is defined and can be written/read, but the full preamble flow (in-memory serialization, blob reference encoding) is deferred.

Replication Log Column Family (ShallowL0Store)

Deferred. The blog format supports a "rlog" role hint for replication log storage, with sparse indexes in footer records and in-memory index maintenance. See design notes in the original proposal.

Implementation Status

Resolved (from original Open Questions)

QuestionResolution
Escape sequence size10 bytes: 6 random + 4 derived. First byte not 0x00/0xFF.
Header/footer encoding40-byte fixed header + named property section with 5-byte trailer. Footer uses regular full-format records: index (0xF0), properties (0xF8), file checksum info (0xFC, placeholder), locator (0xFF). Numbered in expected occurrence order with gaps for future types.
Preamble-start record formatFull format, length=0, type 0x02. Minimal -- just the type tag.
WriteBatch record framingSame compact/full format as blob records, type 0x03.
Compact vs full formatCompact: varint ≤ 2B (payload < 16 KiB), matches compact_record_type, no type byte. Full: varint > 2B (possibly irregular), includes type + pre-payload compression_type + prefix_checksum with context modifier.
Checksum contextAll checksums (fixed header, property section, record prefix, record trailer) use context checksum modifiers with the incarnation ID, ensuring nonzero stored values even with kNoChecksum.
Schema versionStarts at 0. Newer versions are rejected by older readers (returns NotSupported). Checksum is verified before schema version is checked.
Streaming compression sentinelkStreamingCompressionSentinel = 0x7F defined in CompressionType enum (compression_type.h). Used in blog record trailers for streaming-compressed WriteBatch payloads.

DB Options

All are immutable DBOptions. Existing legacy WAL and blob files are always readable regardless of these settings.

Compression Manager Integration