prometheus/tsdb/docs/usage.md

5.0 KiB

Usage

TSDB can be - and is - used by other applications such as Cortex and Thanos. This directory contains documentation for any developers who wish to work on or with TSDB.

For a full example of instantiating a database, adding and querying data, see the tsdb example in the docs. tsdb/db_test.go also demonstrates various specific usages of the TSDB library.

Instantiating a database

Callers should use tsdb.Open to open a TSDB (the directory may be new or pre-existing). This returns a *tsdb.DB which is the actual database.

A DB has the following main components:

The Head is responsible for a lot. Here are its main components:

  • WAL (Write Ahead Log).
  • stripeSeries: this holds all the active series by linking to memSeries by an ID (aka "ref") and by labels hash.
  • Postings list (reverse index): For any label-value pair, holds all the corresponding series refs. Used for queries.
  • Tombstones.

Adding data

Use db.Appender() to obtain an "appender". The golang docs speak mostly for themselves.

Remember:

  • Use Commit() to add the samples to the DB and update the WAL.
  • Create a new appender each time you commit.
  • Appenders are not concurrency safe, but scrapes run concurrently and as such, leverage multiple appenders concurrently. This reduces contention, although Commit() contend the same critical section (writing to the WAL is serialized), and may inflate append tail latency if multiple appenders try to commit at the same time.

Append may reject data due to these conditions:

  1. timestamp < minValidTime where minValidTime is the highest of:
  • the maxTime of the last block (i.e. the last truncation time of Head) - updated via Head.Truncate() and DB.compactHead()
  • tsdb.min-block-duration/2 older than the max time in the Head block. Note that while technically storage.tsdb.min-block-duration is configurable, it's a hidden option and changing it is discouraged. So We can assume this value to be 2h.

Breaching this condition results in "out of bounds" errors.
The first condition assures the block that will be generated doesn't overlap with the previous one (which simplifies querying)
The second condition assures the sample won't go into the so called "compaction window", that is the section of the data that might be in process of being saved into a persistent block on disk. (because that logic runs concurrently with ingestion without a lock) 2) The labels don't validate. (if the set is empty or contains duplicate label names) 3) If the sample, for the respective series (based on all the labels) is out of order or has a different value for the last (highest) timestamp seen. (results in storage.ErrOutOfOrderSample and storage.ErrDuplicateSampleForTimestamp respectively)

Commit() may also refuse data that is out of order with respect to samples that were added via a different appender.

Querying data

Use db.Querier() to obtain a "querier". The golang docs speak mostly for themselves.

Remember:

  • A querier can only see data that was committed when it was created. This limits the lifetime of a querier.
  • A querier should be closed when you're done with it.
  • Use mint/maxt to avoid loading unneeded data.

Example code

Find the example code for ingesting samples and querying them in tsdb/example_test.go