Barograph (barograph v0.1.0)

Copy Markdown View Source

Time-series and event analytics for Elixir, stored in SQLite.

One file. No server. Full SQL.

Summary

Types

A handle to an open Barograph database.

Functions

Closes an open database, terminating its writer and read pool.

Creates a continuous aggregate over a metric (spec §8).

Synchronously commits any buffered samples. Rarely needed by callers (batching is automatic); useful in tests and before shutdown.

Opens (or creates) a Barograph database at path.

Queries a metric, optionally bucketed and aggregated (spec §9.2 level 1).

Forces an immediate refresh of all continuous aggregates. Normally handled by the refresher on each aggregate's :refresh_every interval; useful in tests and after bulk imports.

Runs raw SQL against the database (spec §9.2 level 3).

Returns the database's time unit (:second, :millisecond, or :microsecond).

Writes a single sample, timestamped now in the database's time unit.

Writes a single sample with an explicit timestamp (integer epoch in the database's time unit).

Writes a batch of samples. Each entry is {metric, labels, value} or {metric, labels, value, ts}.

Types

db()

@type db() :: {:via, Registry, {Barograph.Registry, {:database, String.t()}}}

A handle to an open Barograph database.

A :via tuple registered in Barograph.Registry, usable anywhere a GenServer name is accepted.

Functions

close(db)

@spec close(db()) :: :ok

Closes an open database, terminating its writer and read pool.

The file itself is untouched.

create_continuous_aggregate(db, name, opts)

@spec create_continuous_aggregate(db(), String.t(), keyword()) ::
  :ok | {:error, term()}

Creates a continuous aggregate over a metric (spec §8).

Barograph.create_continuous_aggregate(db, "cpu_1h",
  from: "cpu_usage",
  bucket: {1, :hour},
  refresh_lag: {5, :minute},
  refresh_every: {1, :minute}
)

Creates the rollup table bg_agg_<name> storing partial aggregate state (count/sum, never avg) and registers it for periodic watermark-bounded refresh. Query it with plain SQL via sql/3.

flush(db)

@spec flush(db()) :: :ok

Synchronously commits any buffered samples. Rarely needed by callers (batching is automatic); useful in tests and before shutdown.

open(path, opts \\ [])

@spec open(
  Path.t(),
  keyword()
) :: {:ok, db()} | {:error, term()}

Opens (or creates) a Barograph database at path.

If the file does not exist, it is created and initialised with the Barograph schema. If it exists and was written by Barograph, its metadata is checked and the database is opened. Anything else is rejected.

Options

  • :time_unit - :second (default), :millisecond, or :microsecond. Fixed at creation; must match when reopening an existing database.

Examples

{:ok, db} = Barograph.open("/var/data/metrics.bg")

query(db, metric, opts \\ [])

@spec query(db(), String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}

Queries a metric, optionally bucketed and aggregated (spec §9.2 level 1).

Barograph.query(db, "engine_temp",
  labels: %{forklift: "FL-07"},
  from: ~U[2026-07-01 00:00:00Z],
  to: ~U[2026-07-19 00:00:00Z],
  bucket: {1, :hour},
  agg: :avg
)

With :bucket (and :agg, default :avg), returns {:ok, [%{bucket:, value:}, ...]}. Without :bucket, returns raw samples as %{ts, value} maps. See Barograph.Query.run/3 for all options.

refresh_aggregates(db)

@spec refresh_aggregates(db()) :: :ok

Forces an immediate refresh of all continuous aggregates. Normally handled by the refresher on each aggregate's :refresh_every interval; useful in tests and after bulk imports.

sql(db, sql, params \\ [])

@spec sql(db(), String.t(), [term()]) ::
  {:ok, [%{required(String.t()) => term()}]} | {:error, term()}

Runs raw SQL against the database (spec §9.2 level 3).

Always available, never second-class: every design decision in Barograph keeps the file queryable with plain SQL. Positional parameters (?1, ?2, …) are supported. Returns rows as maps of column name to value.

{:ok, rows} = Barograph.sql(db, "SELECT * FROM bg_series")

time_unit(db)

@spec time_unit(db()) :: atom()

Returns the database's time unit (:second, :millisecond, or :microsecond).

write(db, metric, labels, value)

@spec write(db(), String.t(), map(), number()) :: :ok | {:error, :overloaded}

Writes a single sample, timestamped now in the database's time unit.

Barograph.write(db, "engine_temp", %{forklift: "FL-07"}, 94.2)

Returns {:error, :overloaded} if the writer's buffer is full (spec §7.2); the caller decides whether to drop or retry.

write(db, metric, labels, value, ts)

@spec write(db(), String.t(), map(), number(), integer() | nil) ::
  :ok | {:error, :overloaded}

Writes a single sample with an explicit timestamp (integer epoch in the database's time unit).

Writes are idempotent: a duplicate (series, timestamp) pair replaces the previous value (spec §7.5).

write_many(db, samples)

@spec write_many(db(), [tuple()]) :: :ok | {:error, :overloaded}

Writes a batch of samples. Each entry is {metric, labels, value} or {metric, labels, value, ts}.