# WAL Disk Format The write ahead log operates in segments that are numbered and sequential, e.g. `000000`, `000001`, `000002`, etc., and are limited to 128MB by default. A segment is written to in pages of 32KB. Only the last page of the most recent segment may be partial. A WAL record is an opaque byte slice that gets split up into sub-records should it exceed the remaining space of the current page. Records are never split across segment boundaries. If a single record exceeds the default segment size, a segment with a larger size will be created. The encoding of pages is largely borrowed from [LevelDB's/RocksDB's write ahead log.](https://github.com/facebook/rocksdb/wiki/Write-Ahead-Log-File-Format) Notable deviations are that the record fragment is encoded as: ``` ┌───────────┬──────────┬────────────┬──────────────┐ │ type <1b> │ len <2b> │ CRC32 <4b> │ data │ └───────────┴──────────┴────────────┴──────────────┘ ``` The initial type byte is made up of three components: a 3-bit reserved field, a 1-bit zstd compression flag, a 1-bit snappy compression flag, and a 3-bit type flag. ``` ┌─────────────────┬──────────────────┬────────────────────┬──────────────────┐ │ reserved <3bit> │ zstd_flag <1bit> │ snappy_flag <1bit> │ type_flag <3bit> │ └─────────────────┴──────────────────┴────────────────────┴──────────────────┘ ``` The lowest 3 bits within this flag represent the record type as follows: * `0`: rest of page will be empty * `1`: a full record encoded in a single fragment * `2`: first fragment of a record * `3`: middle fragment of a record * `4`: final fragment of a record ## Record encoding The records written to the write ahead log are encoded as follows: ### Series records Series records encode the labels that identifies a series and its unique ID. ``` ┌────────────────────────────────────────────┐ │ type = 1 <1b> │ ├────────────────────────────────────────────┤ │ ┌─────────┬──────────────────────────────┐ │ │ │ id <8b> │ n = len(labels) │ │ │ ├─────────┴────────────┬─────────────────┤ │ │ │ len(str_1) │ str_1 │ │ │ ├──────────────────────┴─────────────────┤ │ │ │ ... │ │ │ ├───────────────────────┬────────────────┤ │ │ │ len(str_2n) │ str_2n │ │ │ └───────────────────────┴────────────────┘ │ │ . . . │ └────────────────────────────────────────────┘ ``` ### Sample records Sample records encode samples as a list of triples `(series_id, timestamp, value)`. Series reference and timestamp are encoded as deltas w.r.t the first sample. The first row stores the starting id and the starting timestamp. The first sample record begins at the second row. ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 2 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┬─────────────┐ │ │ │ id_delta │ timestamp_delta │ value <8b> │ │ │ └────────────────────┴───────────────────────────┴─────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` ### Tombstone records Tombstone records encode tombstones as a list of triples `(series_id, min_time, max_time)` and specify an interval for which samples of a series got deleted. ``` ┌─────────────────────────────────────────────────────┐ │ type = 3 <1b> │ ├─────────────────────────────────────────────────────┤ │ ┌─────────┬───────────────────┬───────────────────┐ │ │ │ id <8b> │ min_time │ max_time │ │ │ └─────────┴───────────────────┴───────────────────┘ │ │ . . . │ └─────────────────────────────────────────────────────┘ ``` ### Exemplar records Exemplar records encode exemplars as a list of triples `(series_id, timestamp, value)` plus the length of the labels list, and all the labels. The first row stores the starting id and the starting timestamp. Series reference and timestamp are encoded as deltas w.r.t the first exemplar. The first exemplar record begins at the second row. See: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 4 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┬─────────────┐ │ │ │ id_delta │ timestamp_delta │ value <8b> │ │ │ ├────────────────────┴───────────────────────────┴─────────────┤ │ │ │ n = len(labels) │ │ │ ├──────────────────────┬───────────────────────────────────────┤ │ │ │ len(str_1) │ str_1 │ │ │ ├──────────────────────┴───────────────────────────────────────┤ │ │ │ ... │ │ │ ├───────────────────────┬──────────────────────────────────────┤ │ │ │ len(str_2n) │ str_2n │ │ │ │ └───────────────────────┴────────────────┴─────────────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` ### Metadata records Metadata records encode the metadata updates associated with a series. ``` ┌────────────────────────────────────────────┐ │ type = 6 <1b> │ ├────────────────────────────────────────────┤ │ ┌────────────────────────────────────────┐ │ │ │ series_id │ │ │ ├────────────────────────────────────────┤ │ │ │ metric_type <1b> │ │ │ ├────────────────────────────────────────┤ │ │ │ num_fields │ │ │ ├───────────────────────┬────────────────┤ │ │ │ len(name_1) │ name_1 │ │ │ ├───────────────────────┼────────────────┤ │ │ │ len(val_1) │ val_1 │ │ │ ├───────────────────────┴────────────────┤ │ │ │ . . . │ │ │ ├───────────────────────┬────────────────┤ │ │ │ len(name_n) │ name_n │ │ │ ├───────────────────────┼────────────────┤ │ │ │ len(val_n) │ val_n │ │ │ └───────────────────────┴────────────────┘ │ │ . . . │ └────────────────────────────────────────────┘ ``` ### Native histogram records Native histogram records are encoded as a list of histogram samples. Series reference and timestamp are encoded as deltas w.r.t the first histogram sample. The first row stores the starting id and the starting timestamp. The first native histogram sample record begins at the second row. There are several different types of native histogram samples. Integer histogram encoding: ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 9 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┬ │ │ │ id_delta │ timestamp_delta │ │ │ ├────────────────────┴────┬──────────────────────┤ │ │ │ counter_reset_hint <8b> │ schema │ │ │ ├──────────────────────┬──┴──────────────────────┤ │ │ │ zero_threshold <8b> │ zero_count │ │ │ ├──────────────────────┴┬────────────────────────┤ │ │ │ count │ sum │ │ │ ├───────────────────────┴────────┬───────────────┴─────────────┐ │ │ │ len(positive_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(positive_buckets) │ bucket_count │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_buckets) │ bucket_count │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ └────────────────────────────────┴─────────────────────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` There are also integer histograms that have custom buckets, which will always have a schem of 053. Custom bucket native histograms additionally encode a field that specifies the custom values: ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 9 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┬ │ │ │ id_delta │ timestamp_delta │ │ │ ├────────────────────┴────┬──────────────────────┤ │ │ │ counter_reset_hint <8b> │ schema │ │ │ ├──────────────────────┬──┴──────────────────────┤ │ │ │ zero_threshold <8b> │ zero_count │ │ │ ├──────────────────────┴┬────────────────────────┤ │ │ │ count │ sum │ │ │ ├───────────────────────┴────────┬───────────────┴─────────────┐ │ │ │ len(positive_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(positive_buckets) │ bucket_count │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_buckets) │ bucket_count │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(custom_values) │ value <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ └────────────────────────────────┴─────────────────────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` (Note: negative spans and negative buckets will be empty for custom bucket native histograms.) Float histogram encoding: ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 10 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id_delta │ timestamp_delta │ │ │ ├────────────────────┴────┬──────────────────────┤ │ │ │ counter_reset_hint <8b> │ schema │ │ │ ├──────────────────────┬──┴──────────────────────┤ │ │ │ zero_threshold <8b> │ zero_count <8b> │ │ │ ├──────────────────────┴┬────────────────────────┤ │ │ │ count <8b> │ sum <8b> │ │ │ ├───────────────────────┴────────┬───────────────┴─────────────┐ │ │ │ len(positive_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(positive_buckets) │ bucket_count <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_buckets) │ bucket_count <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ └────────────────────────────────┴─────────────────────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` There are also float histograms with custom buckets. ``` ┌──────────────────────────────────────────────────────────────────┐ │ type = 10 <1b> │ ├──────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id <8b> │ timestamp <8b> │ │ │ └────────────────────┴───────────────────────────┘ │ │ ┌────────────────────┬───────────────────────────┐ │ │ │ id_delta │ timestamp_delta │ │ │ ├────────────────────┴────┬──────────────────────┤ │ │ │ counter_reset_hint <8b> │ schema │ │ │ ├──────────────────────┬──┴──────────────────────┤ │ │ │ zero_threshold <8b> │ zero_count <8b> │ │ │ ├──────────────────────┴┬────────────────────────┤ │ │ │ count <8b> │ sum <8b> │ │ │ ├───────────────────────┴────────┬───────────────┴─────────────┐ │ │ │ len(positive_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_spans) │ offset │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ length │ │ │ │ ├─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(positive_buckets) │ bucket_count <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(negative_buckets) │ bucket_count <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ len(custom_values) │ value <8b> │ │ │ ├────────────────────────────────┼─────────────────────────────┤ │ │ │ │ . . . │ │ │ └────────────────────────────────┴─────────────────────────────┘ │ │ . . . │ └──────────────────────────────────────────────────────────────────┘ ``` (Note: negative spans and negative buckets will also be empty for custom bucket float native histograms.)