Configuration Reference

Copy Markdown View Source

All configuration is set under the :timeless_traces application key in config.exs or at runtime via Application.put_env/3.

Options

OptionTypeDefaultDescription
data_dirstring"priv/span_stream"Root directory for blocks and index
storageatom:diskStorage backend (:disk or :memory)
flush_intervalinteger (ms)1_000Buffer auto-flush interval
max_buffer_sizeinteger1_000Max spans before forced flush
query_timeoutinteger (ms)30_000Query timeout
compaction_thresholdinteger500Min raw entries to trigger compaction
compaction_intervalinteger (ms)30_000Compaction check interval
compaction_max_raw_ageinteger (s)60Force compact raw blocks older than this
compaction_formatatom:openzlCompression format (:openzl or :zstd)
merge_compaction_target_sizeinteger2_000Target entries per merged compressed block
merge_compaction_min_blocksinteger4Min small compressed blocks before merge triggers
compression_levelinteger6Compression level (1-22)
index_publish_intervalinteger (ms)2_000Index batch write interval
retention_max_ageinteger (s) or nil604_800 (7 days)Max span age (nil = keep forever)
retention_max_sizeinteger (bytes) or nil536_870_912 (512 MB)Max storage size (nil = unlimited)
retention_check_intervalinteger (ms)300_000 (5 min)Retention check interval
httpboolean or keywordfalseEnable HTTP API

HTTP options

When http is a keyword list:

OptionTypeDefaultDescription
portinteger10428HTTP listen port
bearer_tokenstring or nilnilBearer token for authentication

Full example

# config/config.exs
config :timeless_traces,
  # Storage
  storage: :disk,
  data_dir: "priv/span_stream",

  # Buffer
  flush_interval: 1_000,
  max_buffer_size: 1_000,

  # Compaction
  compaction_threshold: 500,
  compaction_interval: 30_000,
  compaction_max_raw_age: 60,
  compaction_format: :openzl,
  compression_level: 6,
  merge_compaction_target_size: 2_000,
  merge_compaction_min_blocks: 4,

  # Indexing
  index_publish_interval: 2_000,

  # Query
  query_timeout: 30_000,

  # Retention
  retention_max_age: 7 * 86_400,
  retention_max_size: 512 * 1_048_576,
  retention_check_interval: 300_000,

  # HTTP API
  http: [port: 10428, bearer_token: "my-secret-token"]

# OTel exporter
config :opentelemetry,
  traces_exporter: {TimelessTraces.Exporter, []}

OpenTelemetry exporter configuration

Wire TimelessTraces as the OTel traces exporter:

config :opentelemetry,
  traces_exporter: {TimelessTraces.Exporter, []}

The exporter reads spans directly from the OTel SDK's ETS table -- no HTTP or protobuf involved. This is significantly lower overhead than sending spans to an external collector.

Storage backends

Disk (default)

Blocks are stored as files in data_dir/blocks/ and the index is persisted as data_dir/index.snapshot + data_dir/index.log:

config :timeless_traces, storage: :disk

Memory

Blocks are stored in ETS tables only. No files are written to disk. Data does not survive restarts. Useful for testing:

config :timeless_traces, storage: :memory

Tuning guidance

Buffer size and flush interval

The defaults (1000 spans / 1 second) balance latency and throughput. For high-volume services:

  • Increase max_buffer_size to 5000-10000 for higher throughput
  • Increase flush_interval to 5000ms to reduce disk I/O
  • Decrease both for lower query latency (spans become queryable after flush)

Compression

The default format is :openzl (columnar compression), which achieves ~10x compression on span data with faster query-time decompression than zstd.

FormatCompression ratioBest for
:openzl~10.0xGeneral use (default), fastest queries
:zstd~6.8xSimpler, fast compression

The compression_level setting applies to both formats (1-22, higher = smaller but slower).

Retention

Both age-based and size-based retention are enabled by default. Set either to nil to disable:

config :timeless_traces,
  retention_max_age: nil,    # No age limit
  retention_max_size: nil    # No size limit

Merge compaction

After initial compaction, many small compressed blocks accumulate (one per flush cycle). Merge compaction consolidates them into larger blocks for better compression and fewer blocks to scan during queries.

  • High volume: the defaults (target 2000 entries, min 4 blocks) work well
  • Low volume: lower merge_compaction_min_blocks to 2 so merges happen with fewer blocks
  • Large queries: increase merge_compaction_target_size to 5000+ to produce fewer, larger blocks

Merge compaction can also be triggered manually via TimelessTraces.merge_now().

Index publish interval

The index journals mutations to a disk log every 2 seconds by default. Lower values mean faster persistence but increase disk I/O. For most workloads the default is fine.