mirror of https://github.com/prometheus/prometheus
commit
63d431b905
@ -0,0 +1,15 @@
|
||||
# Chunks Disk Format
|
||||
|
||||
The following describes the format of a single chunks file, which is created in the `chunks/` directory of a block. The maximum size per segment file is 512MiB.
|
||||
|
||||
Chunks in the files are referenced from the index by the in-file offset in the 4 LSB and the segment sequence number in the bigher 4 MSBs.
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┬──────────────────────┐
|
||||
│ magic(0x85BD40DD) <4 byte> │ version(1) <1 byte> │
|
||||
├────────────────────────────────────────┴──────────────────────┤
|
||||
│ ┌───────────────┬───────────────────┬──────┬────────────────┐ │
|
||||
│ │ len <uvarint> │ encoding <1 byte> │ data │ CRC32 <4 byte> │ │
|
||||
│ └───────────────┴───────────────────┴──────┴────────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
@ -0,0 +1,206 @@
|
||||
# Index Disk Format
|
||||
|
||||
The following describes the format of the `index` file found in each block directory.
|
||||
It is terminated by a table of contents which serves as an entry point into the index.
|
||||
|
||||
```
|
||||
┌────────────────────────────┬─────────────────────┐
|
||||
│ magic(0xBAAAD700) <4b> │ version(1) <1 byte> │
|
||||
├────────────────────────────┴─────────────────────┤
|
||||
│ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ Symbol Table │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Series │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Label Index 1 │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ ... │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Label Index N │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Label Index Table │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Postings 1 │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ ... │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Postings N │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ Postings Table │ │
|
||||
│ ├──────────────────────────────────────────────┤ │
|
||||
│ │ TOC │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
When the index is written, an arbitrary number of padding bytes may be added between the lined out main sections above. When sequentially scanning through the file, any zero bytes after a section's specified length must be skipped.
|
||||
|
||||
Most of the sections described below start with a `len` field. It always specifies the number of bytes just before the trailing CRC32 checksum. The checksum is always calculated over those `len` bytes.
|
||||
|
||||
|
||||
### Symbol Table
|
||||
|
||||
The symbol table holds a sorted list of deduplicated strings that occurred in label pairs of the stored series. They can be referenced from subsequent sections and significantly reduce the total index size.
|
||||
|
||||
The section contains a sequence of the string entries, each prefixed with the string's length in raw bytes. All strings are utf-8 encoded.
|
||||
Strings are referenced by pointing to the beginning of their length field. The strings are sorted in lexicographically ascending order.
|
||||
|
||||
```
|
||||
┌────────────────────┬─────────────────────┐
|
||||
│ len <4b> │ #symbols <4b> │
|
||||
├────────────────────┴─────────────────────┤
|
||||
│ ┌──────────────────────┬───────────────┐ │
|
||||
│ │ len(str_1) <uvarint> │ str_1 <bytes> │ │
|
||||
│ ├──────────────────────┴───────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├──────────────────────┬───────────────┤ │
|
||||
│ │ len(str_n) <uvarint> │ str_1 <bytes> │ │
|
||||
│ └──────────────────────┴───────────────┘ │
|
||||
├──────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
### Series
|
||||
|
||||
The section contains a sequence of series that hold the label set of the series as well as its chunks within the block. The series are sorted lexicographically by their label sets.
|
||||
The file offset to the beginning of a series serves as the series' ID in all subsequent references. Thereby, a sorted list of series IDs implies a lexicographically sorted list of series label sets.
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────┐
|
||||
│ #series <4b> │
|
||||
├───────────────────────────────────────┤
|
||||
│ ┌───────────────────────────────────┐ │
|
||||
│ │ series_1 │ │
|
||||
│ ├───────────────────────────────────┤ │
|
||||
│ │ . . . │ │
|
||||
│ ├───────────────────────────────────┤ │
|
||||
│ │ series_n │ │
|
||||
│ └───────────────────────────────────┘ │
|
||||
└───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Every series entry first holds its number of labels, followed by tuples of symbol table references that contain the label name and value. The label pairs are lexicographically sorted.
|
||||
After the labels, the number of indexed chunks is encoded, followed by a sequence of metadata entries containing the chunks minimum and maximum timestamp and a reference to its position in the chunk file. Holding the time range data in the index allows dropping chunks irrelevant to queried time ranges without accessing them directly.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ len <uvarint> │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────────────┬──────────────────────────────────┐ │
|
||||
│ │ │ ┌──────────────────────────┐ │ │
|
||||
│ │ │ │ ref(l_i.name) <uvarint> │ │ │
|
||||
│ │ #labels │ ├──────────────────────────┤ ... │ │
|
||||
│ │ <uvarint> │ │ ref(l_i.value) <uvarint> │ │ │
|
||||
│ │ │ └──────────────────────────┘ │ │
|
||||
│ ├──────────────────┼──────────────────────────────────┤ │
|
||||
│ │ │ ┌──────────────────────────┐ │ │
|
||||
│ │ │ │ c_i.mint <varint> │ │ │
|
||||
│ │ │ ├──────────────────────────┤ │ │
|
||||
│ │ #chunks │ │ c_i.maxt <varint> │ │ │
|
||||
│ │ <uvarint> │ ├──────────────────────────┤ ... │ │
|
||||
│ │ │ │ ref(c_i.data) <uvarint> │ │ │
|
||||
│ │ │ └──────────────────────────┘ │ │
|
||||
│ └──────────────────┴──────────────────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Label Index
|
||||
|
||||
A label index section indexes the existing (combined) values for one or more label names.
|
||||
The `#names` field determines the number indexed label names, followed by the total number of entries in the `#entries` field. The body holds `#entries` symbol table reference tuples of length of length `#names`. The value tuples are sorted in lexicographically increasing order.
|
||||
|
||||
```
|
||||
┌───────────────┬────────────────┬────────────────┐
|
||||
│ len <4b> │ #names <4b> │ #entries <4b> │
|
||||
├───────────────┴────────────────┴────────────────┤
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ ref(value_0) <4b> │ │
|
||||
│ ├─────────────────────────────────────────────┤ │
|
||||
│ │ ... │ │
|
||||
│ ├─────────────────────────────────────────────┤ │
|
||||
│ │ ref(value_n) <4b> │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ . . . │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
The sequence of label index sections is finalized by an offset table pointing to the beginning of each label index section for a given set of label names.
|
||||
|
||||
### Postings
|
||||
|
||||
Postings sections store monotinically increasing lists of series references that contain a given label pair associated with the list.
|
||||
|
||||
```
|
||||
┌────────────────────┬────────────────────┐
|
||||
│ len <4b> │ #entries <4b> │
|
||||
├────────────────────┴────────────────────┤
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ ref(series_1) <4b> │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ ... │ │
|
||||
│ ├─────────────────────────────────────┤ │
|
||||
│ │ ref(series_n) <4b> │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
The sequence of postings sections is finalized by an offset table pointing to the beginning of each postings section for a given set of label names.
|
||||
|
||||
### Offset Table
|
||||
|
||||
An offset table stores a sequence of entries that maps a list of strings to an offset. They are used to track label index and postings sections. They are read into memory when an index file is loaded.
|
||||
|
||||
```
|
||||
┌─────────────────────┬────────────────────┐
|
||||
│ len <4b> │ #entries <4b> │
|
||||
├─────────────────────┴────────────────────┤
|
||||
│ ┌──────────────────────────────────────┐ │
|
||||
│ │ n = #strs <uvarint> │ │
|
||||
│ ├──────────────────────┬───────────────┤ │
|
||||
│ │ len(str_1) <uvarint> │ str_1 <bytes> │ │
|
||||
│ ├──────────────────────┴───────────────┤ │
|
||||
│ │ ... │ │
|
||||
│ ├──────────────────────┬───────────────┤ │
|
||||
│ │ len(str_n) <uvarint> │ str_n <bytes> │ │
|
||||
│ ├──────────────────────┴───────────────┤ │
|
||||
│ │ offset <uvarint> │ │
|
||||
│ └──────────────────────────────────────┘ │
|
||||
│ . . . │
|
||||
├──────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
### TOC
|
||||
|
||||
The table of contents serves as an entry point to the entire index and points to various sections in the file.
|
||||
If a reference is zero, it indicates the respective section does not exist and empty results should be returned upon lookup.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ ref(symbols) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ref(series) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ref(label indices start) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ref(label indices table) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ref(postings start) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ref(postings table) <8b> │
|
||||
├─────────────────────────────────────────┤
|
||||
│ CRC32 <4b> │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
@ -0,0 +1,157 @@
|
||||
package tsdb
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// enbuf is a helper type to populate a byte slice with various types.
|
||||
type encbuf struct {
|
||||
b []byte
|
||||
c [binary.MaxVarintLen64]byte
|
||||
}
|
||||
|
||||
func (e *encbuf) reset() { e.b = e.b[:0] }
|
||||
func (e *encbuf) get() []byte { return e.b }
|
||||
func (e *encbuf) len() int { return len(e.b) }
|
||||
|
||||
func (e *encbuf) putString(s string) { e.b = append(e.b, s...) }
|
||||
func (e *encbuf) putBytes(b []byte) { e.b = append(e.b, b...) }
|
||||
func (e *encbuf) putByte(c byte) { e.b = append(e.b, c) }
|
||||
|
||||
func (e *encbuf) putBE32int(x int) { e.putBE32(uint32(x)) }
|
||||
func (e *encbuf) putBE64int(x int) { e.putBE64(uint64(x)) }
|
||||
func (e *encbuf) putUvarint32(x uint32) { e.putUvarint64(uint64(x)) }
|
||||
func (e *encbuf) putUvarint(x int) { e.putUvarint64(uint64(x)) }
|
||||
|
||||
func (e *encbuf) putBE32(x uint32) {
|
||||
binary.BigEndian.PutUint32(e.c[:], x)
|
||||
e.b = append(e.b, e.c[:4]...)
|
||||
}
|
||||
|
||||
func (e *encbuf) putBE64(x uint64) {
|
||||
binary.BigEndian.PutUint64(e.c[:], x)
|
||||
e.b = append(e.b, e.c[:8]...)
|
||||
}
|
||||
|
||||
func (e *encbuf) putUvarint64(x uint64) {
|
||||
n := binary.PutUvarint(e.c[:], x)
|
||||
e.b = append(e.b, e.c[:n]...)
|
||||
}
|
||||
|
||||
func (e *encbuf) putVarint64(x int64) {
|
||||
n := binary.PutVarint(e.c[:], x)
|
||||
e.b = append(e.b, e.c[:n]...)
|
||||
}
|
||||
|
||||
// putVarintStr writes a string to the buffer prefixed by its varint length (in bytes!).
|
||||
func (e *encbuf) putUvarintStr(s string) {
|
||||
b := *(*[]byte)(unsafe.Pointer(&s))
|
||||
e.putUvarint(len(b))
|
||||
e.putString(s)
|
||||
}
|
||||
|
||||
// putHash appends a hash over the buffers current contents to the buffer.
|
||||
func (e *encbuf) putHash(h hash.Hash) {
|
||||
h.Reset()
|
||||
_, err := h.Write(e.b)
|
||||
if err != nil {
|
||||
panic(err) // The CRC32 implementation does not error
|
||||
}
|
||||
e.b = h.Sum(e.b)
|
||||
}
|
||||
|
||||
// decbuf provides safe methods to extract data from a byte slice. It does all
|
||||
// necessary bounds checking and advancing of the byte slice.
|
||||
// Several datums can be extracted without checking for errors. However, before using
|
||||
// any datum, the err() method must be checked.
|
||||
type decbuf struct {
|
||||
b []byte
|
||||
e error
|
||||
}
|
||||
|
||||
func (d *decbuf) uvarint() int { return int(d.uvarint64()) }
|
||||
func (d *decbuf) be32int() int { return int(d.be32()) }
|
||||
|
||||
func (d *decbuf) uvarintStr() string {
|
||||
l := d.uvarint64()
|
||||
if d.e != nil {
|
||||
return ""
|
||||
}
|
||||
if len(d.b) < int(l) {
|
||||
d.e = errInvalidSize
|
||||
return ""
|
||||
}
|
||||
s := yoloString(d.b[:l])
|
||||
d.b = d.b[l:]
|
||||
return s
|
||||
}
|
||||
|
||||
func (d *decbuf) varint64() int64 {
|
||||
if d.e != nil {
|
||||
return 0
|
||||
}
|
||||
x, n := binary.Varint(d.b)
|
||||
if n < 1 {
|
||||
d.e = errInvalidSize
|
||||
return 0
|
||||
}
|
||||
d.b = d.b[n:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (d *decbuf) uvarint64() uint64 {
|
||||
if d.e != nil {
|
||||
return 0
|
||||
}
|
||||
x, n := binary.Uvarint(d.b)
|
||||
if n < 1 {
|
||||
d.e = errInvalidSize
|
||||
return 0
|
||||
}
|
||||
d.b = d.b[n:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (d *decbuf) be64() uint64 {
|
||||
if d.e != nil {
|
||||
return 0
|
||||
}
|
||||
if len(d.b) < 4 {
|
||||
d.e = errInvalidSize
|
||||
return 0
|
||||
}
|
||||
x := binary.BigEndian.Uint64(d.b)
|
||||
d.b = d.b[8:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (d *decbuf) be32() uint32 {
|
||||
if d.e != nil {
|
||||
return 0
|
||||
}
|
||||
if len(d.b) < 4 {
|
||||
d.e = errInvalidSize
|
||||
return 0
|
||||
}
|
||||
x := binary.BigEndian.Uint32(d.b)
|
||||
d.b = d.b[4:]
|
||||
return x
|
||||
}
|
||||
|
||||
func (d *decbuf) decbuf(l int) decbuf {
|
||||
if d.e != nil {
|
||||
return decbuf{e: d.e}
|
||||
}
|
||||
if l > len(d.b) {
|
||||
return decbuf{e: errInvalidSize}
|
||||
}
|
||||
r := decbuf{b: d.b[:l]}
|
||||
d.b = d.b[l:]
|
||||
return r
|
||||
}
|
||||
|
||||
func (d *decbuf) err() error { return d.e }
|
||||
func (d *decbuf) len() int { return len(d.b) }
|
||||
func (d *decbuf) get() []byte { return d.b }
|
Loading…
Reference in new issue