Skip to content

obs(selfhost): monitor Cloudflare D1 database size and add bounded dedup retention for signal_snapshots #3810

Description

@JSONbored

Context

Found during the 2026-07-06 incident audit (parent: #1667). The central Cloudflare D1 database (shared by the cloud gittensory-api / Orb) hit its ~10GB size cap today, because signal_snapshots (a contributor/repo scoring signal cache, migrations/0003_data_spine.sql) had zero deduplication — a new snapshot is generated every 6 hours per key with no "keep only latest" logic, accumulating 342,243 rows for only 2,183 distinct keys before the cap was hit. This caused real D1 writes (including an Orb relay registration handshake) to start failing/timing out, contributing to today's outage. It was manually cleaned up today (down to ~3.35GB) as an emergency stopgap, but nothing prevents it recurring.

Grepped the whole repo (src/**, prometheus/, grafana/) for any D1-size signal and found zero — self-host's own Prometheus stack has no scrape target that could see it (D1 size is a central-cloud resource, not self-host-observable), and there's no scheduled check anywhere. Nothing would have caught this before writes started failing in production.

Requirements

  • Add a bounded retention/dedup job for signal_snapshots: keep only the latest row per (signal_type, target_key), since the table only needs current state per key, not full history. This is the actual root-cause fix — without it, the table refills toward the cap again regardless of monitoring.
  • Add a scheduled probe that fetches current D1 database size (and row counts for high-growth tables) via the Cloudflare API, since size isn't queryable from inside a D1 query itself.
  • Expose the result as a new gauge metric (e.g. gittensory_d1_database_size_bytes, gittensory_d1_table_row_count{table=...}).
  • Add a Prometheus alert for D1 size approaching the cap (warn ~70%, critical ~90%) and for unbounded per-key row growth in tables like signal_snapshots.
  • Add a corresponding Grafana panel.

Deliverables

  • Dedup/retention job or migration for signal_snapshots (bounded batches — a naive single-statement window-function delete over the full table will exceed D1's per-statement CPU limit, confirmed empirically today; batch by signal_type or similar).
  • src/selfhost/metrics.ts: new gauge metric definitions.
  • New scheduled probe wired into the existing scheduled-loop or its own Sentry monitor.
  • prometheus/rules/alerts.yml + grafana/dashboards/gittensory.json: new rule + panel.

Expected outcome

signal_snapshots (and similar tables) stay bounded in size going forward instead of accumulating indefinitely, and an operator sees D1 size trending toward the cap in Grafana well before writes start failing — instead of discovering it only after a production outage.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions