ReductStore
Dedicated time-series blob store for snapshot history and audit binaries.
ReductStore is a time-series blob database. The dashboard uses it for snapshot history — the JPEG-per-feed-per-N-seconds archive that backs the snapshot scrub on every video tile.
It runs as a dedicated cluster (commit 95521d3f separated it from the
shared store) so heavy snapshot reads can't starve other dashboard data.
Where it sits
| In-cluster URL | (set via REDUCTSTORE_URL env on the dashboard) |
| Chart / values | r2d2-fleet/k8s/reductstore-snapshots/ in the parent repo |
| Bucket layout | one bucket per feed, time-indexed |
| Retention | (configured per bucket — typically days, not months) |
Reads / writes from the dashboard
| Direction | Route handler | What flows |
|---|---|---|
| Write | /api/restreaming/snapshots (POST) | JPEG blob + feed ID + timestamp |
| Read latest | /api/restreaming/snapshots/latest?feedId=… | The most recent JPEG for poster fallback |
| Read range | /api/restreaming/snapshots/history?feedId=…&from=…&to=… | A list of timestamps + thumbnails for the scrub |
| Read one | /api/restreaming/snapshots/at?feedId=…&ts=… | The single JPEG at a timestamp |
The TypeScript wrapper lives in src/lib/reductstore.ts.
Why a separate store (and not TrailBase)
- Blob size. Snapshots are JPEGs in the 50–500KB range; SQLite is the wrong tool for that volume.
- Time-indexed reads. Snapshot history is "give me everything between T1 and T2" — ReductStore is built for this; SQLite needs a range query + blob join per row.
- Retention. ReductStore's bucket-level retention drops old blobs automatically; in SQLite we'd build that ourselves.
- Workload isolation. A long history scrub shouldn't block panel CRUD. Two clusters, two failure domains.
Snapshot capture flow
flowchart LR
T[Tile mounted, page visible] --> P[Periodic capture trigger]
P --> F[Fetch from Restreamer snapshot endpoint]
F --> R[POST /api/restreaming/snapshots]
R --> RD[ReductStore bucket write]
T -.->|tile re-render with snapshot| RDCapture is triggered by the dashboard itself on focus / interval — there's no separate cron. This means an inactive dashboard doesn't accumulate snapshots; if you need guaranteed-rate capture, that's a future change.
Audit binaries
A planned use case beyond snapshots — large audit payloads (full before/after of
a panel mutation, request bodies of bulk imports) that we don't want bloating the
TrailBase audit table. Not yet wired.
What ReductStore is not used for
- Structured metadata. That's TrailBase.
- HLS segments. Restreamer serves HLS directly; we don't archive segments.
- Logs. Logs go to stdout / cluster log pipeline, not here.
See also
- Restreaming — primary consumer
- TrailBase — sibling store for structured data
- hls.js — snapshots as poster fallback