Time-series interface¶
The tsdb feature (eg-tsdb) gives a native, redb-backed time-series store with the analytic
primitives a time-series workload needs — including ASOF joins, which DataFusion 43 lacks. It is
pure-Rust and shares the engine's one durability model.
Status snapshot: the store and time-ops are supported as native functions, now bound into the unified planner (
Op::Windowexecution, EG-KG.query.streaming-execution) with per-point retention trim (EG-KG.temporal.bucket-cutoff-trim). A columnar segment layout backs analytical/SQL-window scans (EG-089), and a PromQL/api/v1/queryHTTP API (EG-172) serves the metric series. TimescaleDB hypertables + continuous aggregates (EG-117) layer on over pgwire. See the capability matrix.
The store¶
SeriesStore keeps columnar chunks under a composite redb key
(canonical_scope, bucket_start), where canonical_scope is a versioned,
length-prefixed (tenant, graph, series_id) identity. Public Ts* requests always pass
through the target graph's ACL and placement route before the store is opened; TsAppend
requires write access and the range/ASOF/window/gap-fill operations require read access.
The same local series name can therefore be used in two tenant graphs without sharing
metadata or points.
append_scopedamortizes the fsync across a group of points and commits its projection cursor in the same transaction;range_scopeddoes a bounded scan over a[from, to)window within one tenant/graph scope;evict_before_scopedapplies retention, trimming straddling buckets at the boundary (per-point trim, EG-KG.temporal.bucket-cutoff-trim), not just whole-bucket eviction.
Cross-modal transactions persist this same canonical key in the authoritative
authoritative shard copy. Startup reconciliation records a durable count/timestamp-span high-water
cursor in series.redb; an interrupted projection is reported as catching_up or
degraded and is replayed without duplicating points.
| Validation | Coverage |
|---|---|
| Canonical-key collision | Equal local ids in different tenants return different points. |
| Negative authorization | An agent cannot read another agent graph's identically named series. |
| Restart | Canonical keys and the durable projection cursor survive store reopen. |
| Crash reconciliation | Authoritative points replay once; a second pass is a no-op. |
A columnar (struct-of-arrays) segment layout (EG-089) backs analytical scans over the tsdb/table store — the same layout the SQL window functions read (see sql).
Time operations (native, no DataFusion)¶
| Function | What it does |
|---|---|
time_bucket |
aligned buckets (ts/w)*w with avg / min / max / sum / count / first / last |
asof_join_backward |
ASOF (as-of-or-before) join with tolerance, O(L+R) merge |
gap_fill_locf |
last-observation-carried-forward over a fixed grid |
ohlc_bars |
open / high / low / close + volume bars |
downsample |
rollup to a coarser resolution |
decay_weighted_mean |
Ebbinghaus decay-weighted mean (shared eg_core::decay curve) |
series_ewma, series_rolling_zscore |
EWMA and rolling z-score (reuse the finance kernels) |
Unified-planner binding (EG-KG.query.streaming-execution)¶
The time-ops are wired behind the planner's Op::Window (with the eg-plan→eg-tsdb dependency edge +
cost-based reorder), so a temporal aggregate composes inline in a UQL pipeline with
graph/vector/SQL stages. The bi-temporal Op::AsOf point-in-time filter also executes today:
PromQL / Prometheus HTTP API (EG-172, feature promql on the obs listener)¶
A PromQL evaluator (instant/range vectors, selectors, rate/sum/avg/max/min/histogram functions,
binary ops) over the eg-tsdb metric series is exposed as a Prometheus-compatible HTTP API on the obs
listener (EPISTEMIC_GRAPH_OBS_ADDR, default 127.0.0.1:5080):
curl -s 'http://127.0.0.1:5080/api/v1/query?query=up'
curl -s 'http://127.0.0.1:5080/api/v1/query_range?query=rate(http_requests[5m])&start=…&end=…&step=15s'
Point a Grafana Prometheus data source at the obs listener. This is one leg of the full observability surface (logs + PromQL + traces) — see observability.
TimescaleDB compatibility (EG-117, over pgwire)¶
CREATE EXTENSION timescaledb lights up create_hypertable(), time_bucket() gap-fill, and
CREATE MATERIALIZED VIEW … WITH (timescaledb.continuous) continuous aggregates, lowered onto this
time-series store + Op::Window — see sql.
See also: Capabilities matrix · SQL & pgwire · Observability · Messaging & Broker · Connecting (per-wire guide).