blob: b32c2bedd3fab09bf3b20cb6249c4f4175f67af2 [file] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
# File Format
A TsFile stores time series data in columns. Writers append data in device groups, then write
the indexes at the end of the file. Readers usually work in the opposite direction: they open
the footer, look up a series, and seek to the Chunks that contain its values.
This guide describes the v4 file layout, identified by version byte `0x04`. It covers the file
structures and how they fit together. The encoding chapter gives codec identifiers and the
`PLAIN` layout; the bit layouts of the other value codecs are outside its scope.
## Scope and Terminology
Data has three levels of organization within a file:
- A **Page** holds a batch of encoded values. Each Page is compressed independently.
- A **Chunk** holds the Pages of one physical column. Its header identifies the data type,
encoding, and compression method.
- A **Chunk Group** holds a batch of Chunks for one device. The same device can appear in several
Chunk Groups as the writer flushes more data.
In **non-aligned** data, each measurement stores its own timestamps alongside its values. In
**aligned** data, a Time Chunk stores the timestamps shared by several Value Chunks. A bitmap in
each Value Page identifies the positions that have values. [Data Pages](DataPages/README.md)
explains both layouts.
<TsFileStructureTree locale="en" />
## Physical Regions
The file begins with the six-byte magic string `TsFile` and a one-byte version. An optional
file encryption header follows for Page AEAD files. The data section ends at a single `0x02`
separator; indexes and file metadata follow it. The last ten bytes hold the metadata length and
the trailing magic string.
The table below uses these names:
- `F`: total file length.
- `L`: serialized length of `TsFileMetadata`, stored in the four-byte trailing length field.
- `E`: total serialized length of the optional encryption header, or `0` when absent.
- `data_start = 7 + E`: start of the data section.
- `meta_offset`: separator offset, stored inside `TsFileMetadata`.
| Region | Inclusive byte interval | How it is located |
| --- | --- | --- |
| Header magic | `0 … 5` | Fixed position |
| Version | `6` | Fixed position |
| Optional file encryption header | `7 … 6 + E` | Present only for Page AEAD; marker `0x06` |
| Data section | `data_start … meta_offset - 1` | Ends immediately before the separator |
| Separator | `meta_offset` | `0x02`, referenced by `TsFileMetadata` |
| Indexed metadata | `meta_offset + 1 … F - 11 - L` | Delimited by metadata offsets |
| `TsFileMetadata` | `F - 10 - L … F - 11` | Located from the trailing length |
| Metadata length | `F - 10 … F - 7` | Fixed position relative to the end |
| Tail magic | `F - 6 … F - 1` | Fixed position relative to the end |
All offsets are absolute, starting at the first byte of the file. The table uses inclusive
intervals; `[start, end)` elsewhere denotes a half-open interval. A length excludes its own
length field unless stated otherwise. The optional-header interval is absent when `E = 0`.
Timeseries Metadata and index nodes can be interleaved in the indexed metadata section. Readers
follow their offsets rather than assuming a fixed physical order. `TsFileMetadata` has no marker
of its own: its length locates it from the end of the file.
## Write Model
A writer appends Chunk Groups, then writes the indexes and footer when it finishes the file.
Page sizes, flush thresholds, and index fanout affect where structures begin and end, but readers
can recover those boundaries from headers and offsets. Timestamp units and non-aligned time
encoding need the shared context described in [Configurations](Configurations.md).
Both magic strings use the same bytes:
```text
54 73 46 69 6c 65 # "TsFile"
```
## Metadata-first Read Algorithm
A reader can locate the requested series without scanning the data section:
1. Check the leading magic and version. Inspect offset `7` for an optional
[file encryption header](DataPages/Encryption.md#file-encryption-header), which determines
how Chunk Headers and Page bodies are parsed.
2. Check the tail magic, read `L` at `F - 10`, and calculate `file_metadata_pos = F - 10 - L`.
3. Read `TsFileMetadata` and check that `meta_offset` points to the separator.
4. Optionally check the complete path in the [Bloom filter](Bloom-Filter.md). Use a negative
result only when the writer's charset, path construction, and hash behavior are known to
match.
5. Follow the table/device index, then the measurement index, to find the series metadata.
6. Use the series and Chunk Statistics to select Chunk offsets. Seek to those Chunks and use
any Page Statistics to narrow the selection further.
7. Read the selected Page bodies, decrypt when required, decompress, and decode their streams.
This avoids reading the payloads of unrelated Pages. A single-Page Chunk omits Page Statistics,
so pruning at that level relies on the Chunk or series Statistics instead.
## Structural Invariants
The main relationships to check when reading are:
- Both magic strings and the version are valid.
- A file encryption header, when present, appears once at offset `7`; its length and version
are valid.
- `0 <= L <= F - 10`, and `file_metadata_pos` lies after the separator.
- A Chunk's Pages occupy exactly `data_size` bytes after its complete Header.
- Each non-empty Page body occupies exactly `compressed_size` bytes within that Chunk.
- Index offsets are monotonic (non-decreasing) within a node and do not exceed `end_offset`.
- An aligned Value Page covers the same positions as the Time Page with the same ordinal; its
`position_count` equals the Time Page's position count.
A violation is a format error.
[Binary Representation](Binary-Representation.md) describes the common length and offset checks.
[Encryption](DataPages/Encryption.md) adds the authentication checks for Page AEAD. Marker bytes
have meaning only at structure boundaries; the same bytes can occur inside a Page body.
## Format Details
For a first reading, start with [Binary Representation](Binary-Representation.md),
[Chunks](DataPages/Chunks.md), and [Data Pages](DataPages/README.md), then follow the metadata
lookup in [Metadata](Metadata.md).
- [Types](Types.md) and [Encodings](DataPages/Encodings.md)
- [Nulls](Nulls.md) and [Compression](DataPages/Compression.md)
- [Bloom Filter](Bloom-Filter.md)
- [Encryption](DataPages/Encryption.md) and [Checksumming](DataPages/Checksumming.md)
- [Error Recovery](DataPages/Error-Recovery.md)
- [Configurations](Configurations.md), [Format Versions](Format-Versions.md), and
[Extensibility](Extensibility.md)