mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
## What A request that names channels with `#h` used to check each channel missing from the cached access set with its own `is_member` query. These checks ran one after another, so the database cost of a request grew with the number of channels it named. That cost is why the relay capped explicit `#h` values at 128, and why mobile splits large communities into many requests ([#8342](https://github.com/block/buzz/pull/8342)). This PR makes the check one query, and then raises the cap. - **One membership query.** WS REQ, WS COUNT, and HTTP `/query` and `/count` now share `confirm_requested_channel_access`. It checks every unconfirmed channel with one `membership_pairs` statement (`channel_id = ANY($2)`), so one request costs one query, whatever number of channels it names. `membership_pairs` uses the same SQL rules as `is_member`: deleted channels and removed members don't count, and it runs on the writer pool. - **Hash-set lookups.** Comparing the requested channels with the accessible set no longer costs requested × accessible comparisons. This covers `apply_channel_scope_to_query`, the system-channel scope helpers, the COUNT and bridge loops, and `#h` dedupe. - **`MAX_EXPLICIT_CHANNEL_VALUES` goes from 128 to 2048.** Huddle liveness used the same constant to limit session IDs. It now has its own constant, `MAX_HUDDLE_LIVENESS_SESSIONS`, which stays at 128, because each linked session still costs a separate mesh-directory lookup. Authorization rules are unchanged: - A scoped token still sets the upper limit. A channel outside the token is never checked. - Channels already cached and readable system channels are skipped. - Repaired channels are added in request order, each once, so `keep_repaired_system_channels_named` still sees the same repaired tail. - A database error still closes the request (or returns 500 over HTTP). One difference: the old loop could leave a partial repair if a later query failed, and that can no longer happen. ## Not in this PR - **Client chunk sizes.** `desktop/src/shared/api/relayClientShared.ts:39` and `mobile/lib/shared/relay/nostr_filters.dart:8` stay at 128. They can go up only after every relay they talk to runs this change. - **Per-connection limit on live channel scope.** Each authorized channel in a live WS subscription still gets a fan-out index entry for each kind, plus a Redis topic retain. Real membership limits this, not the cap. Still, an account in at least 2048 channels could now register up to 1024 × 2048 channel scopes on one connection; before, the most was 1024 × 128. If we want to keep the old worst case, the fix is a limit on the total channel scope per connection, not per REQ. ## Testing - `cargo clippy -p buzz-relay --tests` and `cargo fmt` are clean. - `cargo test -p buzz-relay --lib`: 1449 passed, 6 failed. All 6 are in `api::media::tests` and fail because the local Postgres on :5432 has no `buzz` role. Nothing in this change touches them. - I ran the Postgres lane (`scripts/postgres-test-run.sh`) for the bridge, req, count, group-state, and system-channel tests on local PG14: 55 passed. That includes the new `named_channels_missing_from_the_cache_are_confirmed_together`, which checks that the reader's channels missing from the cache are added in request order, each once, and that a stranger's channel stays out. - Not done yet: a live run against a local relay sending a REQ with more than 128 `#h` values. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Bradley Axen <baxen@squareup.com> Co-authored-by: murderbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>