Metrics maintenance
experiment_metrics is the only data in the schema that grows without bound: a
training run that logs every step writes a row per step per metric, forever.
Three migrations exist to keep that from filling the volume, and one of them
only does anything if something runs it.
| Migration | What it does | Runs by itself? |
|---|---|---|
0014 + 0114 |
Narrows a point to 56 bytes and turns experiment_metrics into a view |
on apply |
0115 |
Packs settled points into arrays in experiment_metric_chunks — 132 → 21 bytes/point |
no |
0115 |
experiment_metrics_rollup() moves rows into chunks |
no |
Also: apps/web/src/app/api/sdk/metrics/route.ts thins a series on the way in
(Fix C: every point below step 10 000, then a sampling rate that halves once per
octave), so the stored point count is logarithmic in run length. That is the
ingest path. A writer that talks to PostgREST directly — the Python SDK's
SupabaseMetricRepository — bypasses it, which is what the pruning script below
is a backstop for.
The two commands
# Move settled points into chunks. Safe to run any time; whole chunks only.
npm run rollup:metrics
# Re-apply the ingest sampling rule to series that predate it or bypassed it.
npm run prune:metrics # report only
npm run prune:metrics -- --apply # delete the off-grid points
Both read DATABASE_URL from the environment, falling back to
secrets/.env.migration. Both accept the flags their script's header documents
(--chunk, --hot-tail, --vacuum).
Suggested schedule
Nothing schedules these for you, and the repository deliberately does not pretend otherwise: a cron entry is deployment configuration, and rolling up on the write path was rejected because it would put an array rewrite in front of every ingest.
# Daily: archive what has settled. Cheap — it only touches whole chunks beyond
# the 250-point hot tail of each series.
17 3 * * * cd /opt/weaveforge && npm run rollup:metrics >> /var/log/weaveforge-rollup.log 2>&1
# Weekly: check for series that grew past the sampling rule.
23 3 * * 0 cd /opt/weaveforge && npm run prune:metrics >> /var/log/weaveforge-prune.log 2>&1
Add --vacuum to the monthly run if you need the on-disk figure to drop; it
takes an ACCESS EXCLUSIVE lock, which is why it is opt-in rather than part of
the daily job.
Nothing is lost by not running the rollup — every point stays readable, the
view unions both stores, and the read path (latest_metric_activity,
metric_history) works either way. What you lose is the space: 132 bytes per
point instead of 21.
Checking whether it is working
-- How much of each series is archived rather than loose.
select (select count(*) from experiment_metric_chunks) as chunks,
(select count(*) from experiment_metric_points) as loose_rows;
-- Points still awaiting a rollup per series.
select experiment_id, metric_id, count(*)
from experiment_metric_points
group by experiment_id, metric_id
having count(*) > 500
order by count(*) desc
limit 20;