mirror of
https://github.com/block/buzz.git
synced 2026-11-06 17:31:19 +00:00
## Summary The relay no longer runs database aggregate queries to emit usage gauges. Fleet and per-community counts of users, channels, relay members, workflows, git repos, communities, messages, and active users/channels are no longer relay metrics, and neither are the snapshot health gauges that came with them. ### Removed - **Gauges:** `buzz_total_users`, `buzz_total_channels`, `buzz_total_relay_members`, `buzz_total_workflows`, `buzz_total_git_repos`, `buzz_total_active_users`, `buzz_total_messages`, `buzz_total_active_channels`, `buzz_communities_estimated` / `buzz_communities_total`, and every DB-derived `buzz_community_*` series (users, channels, messages, relay members, workflows, git repos, active users, active channels). - **Per-community mode:** `BUZZ_USAGE_METRICS_PER_COMMUNITY=all` and everything it fed: the per-community in-memory gauges, `buzz_community_storage_bytes` / `buzz_community_storage_objects`, `buzz_storage_unmapped_community_bytes`, the community → host map query, and the emission-scope bookkeeping that cleared series for renamed or removed communities. No deployment set it. If one still does, the relay ignores it. - **Health signals:** `buzz_usage_snapshot_available`, `buzz_usage_snapshot_age_seconds`, `buzz_usage_query_skipped_total`, and the `usage_fleet_*` paths of `buzz_db_route_decision`. - **Code and config:** - `buzz-db/src/store/usage.rs`, including its aggregate queries. - The relay's `FleetUsageSchedule` and its cadence/deadline/retry machinery. - The replica-only telemetry route (`Db::route_usage_read`). - `BUZZ_USAGE_METRICS_REPLICA_MAX_AGE_MS` and `DbConfig::usage_metrics_replica_max_age_ms`. If a deployment still sets the env var, the relay now ignores it; startup doesn't fail. - **Reader maintenance pool operation:** `ReaderOperation::Maintenance` and its `close_on_drop` release path existed only for those telemetry reads, so they're removed too. The pool-acquire contract drops from twelve to eleven valid pairs, which lowers the per-pod series ceiling from 216 to 198. - **Dead code:** `Db::active_community_hosts` had no callers. ### Kept - **Leader:** the usage poller's leader lock, plus the leader's invite reaping and fleet storage-snapshot gauges. The session advisory lock (`SessionLeader`, `Db::try_lock_session_leader`) moves to `buzz-db/src/store/leader_lock.rs`, since storage-usage delivery uses it too and it has nothing to do with usage queries. - **In-memory gauges:** pod-local connection, subscription, and online-user gauges. They read process state, not the database. - **Writer maintenance pool operation:** `WriterOperation::Maintenance` stays. Dozens of writer-side maintenance paths across `buzz-db` use it (reapers, retention, the leader lock). The reader variant only ever served the telemetry reads. - **Storage-usage delivery:** unchanged. It reads the storage snapshot for billing, not for metrics. `docs/usage-metrics.md`, `docs/storage-accounting.md`, `.env.example`, and the Helm chart README are updated to match. ## What got simpler This is net deletion (~3.6k lines). One telemetry subsystem is gone: two collection families with independent cadences, availability and age semantics, relay-side deadlines, a dedicated replica routing policy, two config knobs, a per-community emission mode, and a pool-operation variant. Leader election now does only leader work, and it lives in its own module. ## Operational note Dashboards and monitors that read the removed series will go to no data once this rolls out. ## Testing - `cargo fmt --check`, workspace `cargo clippy --all-targets -D warnings`. - `buzz-db` lib, `observability_source`, and doc tests. - `buzz-relay` lib tests. - The Postgres lane for `buzz-db` (lib + tests) and `buzz-relay` (lib). All of these run on a remote workstation at the PR head. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
5.5 KiB
Markdown
108 lines
5.5 KiB
Markdown
# Storage accounting
|
|
|
|
`buzz-admin storage-snapshot` lists S3, calculates physical and per-community
|
|
logical usage, and atomically replaces the completed snapshot in PostgreSQL.
|
|
The relay reads that snapshot and exposes the existing storage gauges on its
|
|
metrics endpoint for collection by Datadog or another metrics collector.
|
|
|
|
## Independent rollout
|
|
|
|
Deploy the snapshot-reading relay version through the usual release process.
|
|
It is ready before a worker exists: no database row means no storage gauges.
|
|
Add a scheduled worker to each environment when ready. Its first successful
|
|
publication becomes visible on the next leader usage tick, without restarting
|
|
the relay or changing its mode. The default tick interval is 300 seconds.
|
|
|
|
The snapshot table is part of the normal database schema, independent of worker
|
|
installation. A missing table or inaccessible database is an error, not evidence
|
|
that the worker is absent. Snapshot reads have a five-second budget, including
|
|
connection acquisition, and retry on the next usage tick. They do not run on
|
|
the relay startup path and never fall back to an S3 scan.
|
|
|
|
`BUZZ_STORAGE_METRICS` accepts:
|
|
|
|
| Value | Behavior |
|
|
|---|---|
|
|
| Unset, `external`, `snapshot`, `on` | Read completed worker snapshots. |
|
|
| `inline` | Legacy alias for the reader; logs a migration warning once. |
|
|
| `off` | Skip storage reads and metrics. |
|
|
| Anything else | Log a configuration error and disable storage metrics. |
|
|
|
|
Relay-local scans have been removed. Existing chart values that set `inline`
|
|
need no coordinated configuration change. Environments without a worker will
|
|
stop producing storage totals when this relay version arrives. The former
|
|
`BUZZ_STORAGE_SWEEP_*` settings no longer apply; size and bound the worker
|
|
instead. An intentional `off` override still requires an operator to enable it.
|
|
|
|
## Freshness and failures
|
|
|
|
The reader publishes the last completed totals together with
|
|
`buzz_storage_snapshot_age_seconds`. Re-reading an old row never resets its
|
|
completion time. Alert on age using a threshold appropriate for the worker's
|
|
schedule and allowed run time; for a daily worker, allow more than 24 hours.
|
|
`buzz_storage_snapshot_load_ok=1` means the row was read and decoded, not that
|
|
the worker's most recent attempt succeeded. Check Job failures separately.
|
|
|
|
A failed or invalid read logs an error, sets load health to zero, and retains
|
|
the last good totals with their original age. With no prior good snapshot,
|
|
only failed-load health is emitted. A successful read that finds no row clears
|
|
the cache and stops refreshing storage series. Existing exported series expire
|
|
through the metrics exporter's idle timeout; absence is never reported as zero
|
|
bytes. A valid empty snapshot can report zero bytes.
|
|
|
|
The reader emits fleet totals from the leader only; per-community storage is
|
|
not a relay metric. It does not need S3 listing permission. The worker still
|
|
needs that permission and its database configuration.
|
|
|
|
## Usage delivery
|
|
|
|
Set `BUZZ_STORAGE_USAGE_URL` to push each new snapshot's per-community totals
|
|
to an operator endpoint. kgoose's `POST {prefix}/v1/buzz/storage-usage/record`
|
|
receives them and bills sponsored communities in Orb. Unset, nothing is sent.
|
|
Every five minutes, one relay pod (elected by a PostgreSQL session advisory
|
|
lock) compares the stored snapshot's `completed_at` with the last one it
|
|
handled and, when it is new, POSTs every community in consecutive batches of
|
|
at most 50:
|
|
|
|
```json
|
|
{
|
|
"measured_at": "2026-10-30T03:04:12Z",
|
|
"communities": [
|
|
{
|
|
"community_host": "acme.communities.buzz.xyz",
|
|
"storage_bytes": 123456789,
|
|
"storage_objects": 412
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
- `measured_at` is the snapshot's completion time in RFC 3339, the same for
|
|
every batch of a snapshot. Each batch is an independent request.
|
|
- Each request carries a NIP-98 `Authorization` header signed by the relay
|
|
key, binding the URL and body, as operator-listener mention notifications
|
|
do. kgoose authenticates the relay by its `buzz` mesh service identity
|
|
instead, so the relay needs mesh egress to kgoose-builderbot.
|
|
- `storage_bytes` and `storage_objects` are the logical per-community values
|
|
described above: a blob bound to several communities counts in full for
|
|
each.
|
|
- Every snapshot community with a community row is sent, including archived
|
|
or deleting ones that still hold bytes. An active community absent from the
|
|
snapshot is sent with zero, so a receiver keeping the latest value never
|
|
retains a stale one. A snapshot community with no community row has no host
|
|
and is skipped with a warning.
|
|
- A 2xx accepts a batch. A 408, 429, 5xx, or transport error stops the run and
|
|
re-sends the whole snapshot on the next check. Any other status abandons
|
|
that snapshot; the next worker run is delivered normally.
|
|
- Which snapshot was handled is kept in memory, so a restart or leader change
|
|
re-sends the latest snapshot. Receivers MUST treat a repeated community and
|
|
`measured_at` as already recorded; kgoose bills only a community's first
|
|
report for each UTC day.
|
|
- A snapshot replaced before it was delivered is never sent. Alert on
|
|
`time() - buzz_storage_usage_delivered_snapshot_timestamp_seconds` alongside
|
|
snapshot age; `buzz_storage_usage_deliveries_total{outcome}` counts
|
|
`accepted`, `retry`, and `rejected` checks. The leader re-publishes the
|
|
delivered timestamp on every check, so it survives the gauge idle timeout
|
|
between daily snapshots. After a restart or leader change it has no sample
|
|
until the next delivery succeeds, so treat a missing series as failing too.
|