Skip to content

perf(relay,desktop): bulk latest-message-per-channel lookup for get_channels#1465

Open
tlongwell-block wants to merge 2 commits into
mainfrom
perf/last-message-distinct-on
Open

perf(relay,desktop): bulk latest-message-per-channel lookup for get_channels#1465
tlongwell-block wants to merge 2 commits into
mainfrom
perf/last-message-distinct-on

Conversation

@tlongwell-block

Copy link
Copy Markdown
Collaborator

What

get_channels populated last_message_at with one limit:1 filter per channel — N top-1 DB queries per sidebar refresh, re-run every 60s (bounded-concurrent since #1457, but still O(N) queries). This replaces the N filters with one bridge extension filter and one indexed DB round trip. This is the DISTINCT ON follow-up deliberately split out of #1457.

  • buzz-db: get_latest_event_per_channelunnest($channels) CROSS JOIN LATERAL (... ORDER BY created_at DESC, id ASC LIMIT 1) riding idx_events_community_channel_created.
  • bridge /query: new latest_per_channel: true extension field (same raw-JSON pattern as before_id / depth_limit / feed_types), handled in a dedicated phase ahead of the catch-all.
  • desktop get_channels: N filters → {"kinds":[9,40002],"#h":[...all ids],"latest_per_channel":true}. Rust-only change; no collision with perf(desktop): instant channel switching — non-blocking first paint, persisted snapshots #1452's TS surface.

Correctness

  • Same winner per channel. The LATERAL subquery's ordering (created_at DESC, id ASC LIMIT 1) is byte-identical to query_events with limit:1, which is what each per-channel filter executed before. A Postgres-backed equivalence test asserts bulk winners == serial winners, including the same-second id ASC tie-break, soft-delete exclusion, wrong-kind exclusion, and empty-channel omission.
  • Same access enforcement. Requested #h channels are intersected with accessible_channels before the query (same outcome as the catch-all's access-scope skip), and every returned event passes the identical four per-event gates: event_in_accessible_channel, filters_match, reader_authorized_for_event, is_author_only_event.
  • Why the multi-#h hazard doesn't apply. Plain multi-#h pushdown is rejected in this codebase because quieter channels get silently dropped under a shared limit budget. Here top-1-per-channel is guaranteed by construction (LATERAL per unnest row), so there is no shared budget to starve.
  • Kinds semantics follow NIP-01: kinds absent = any kind; empty = match nothing.
  • Opt-in only. Filters without latest_per_channel: true are handled exactly as before; the WS REQ path is untouched.

Plan shape

EXPLAIN shows ordered index scans on (community_id, channel_id, created_at DESC, id) under a Limit node on every partition — no sort. LATERAL was chosen over DISTINCT ON specifically because the per-group LIMIT 1 cannot degrade to a full sort as channel count/history grows.

Verified

  • Postgres-backed equivalence test: cargo test -p buzz-db latest_event_per_channel -- --ignored → green
  • cargo test -p buzz-relay -p buzz-db: 451 + 79 pass, 0 fail (re-run after merging main e42dae3f on top)
  • Desktop tauri tests: 864 pass, 0 fail
  • cargo clippy --workspace --all-targets -- -D warnings: clean

npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d and others added 2 commits July 2, 2026 11:59
…hannels

get_channels populated last_message_at with one limit:1 filter per
channel — N top-1 DB queries per sidebar refresh, re-run every 60s
(bounded-concurrent since #1457, but still O(N) queries).

Replace the N filters with a single bridge extension filter,
`latest_per_channel: true` (same raw-JSON pattern as before_id /
depth_limit / feed_types):

- buzz-db: get_latest_event_per_channel — unnest + LATERAL top-1 per
  channel riding idx_events_community_channel_created. Unlike
  DISTINCT ON, the LATERAL LIMIT 1 cannot degrade to a sort; EXPLAIN
  confirms ordered index scans on every partition. Per-channel
  ordering (created_at DESC, id ASC LIMIT 1) is byte-identical to
  query_events with limit:1, so the winning event per channel is
  unchanged.
- bridge /query: new phase ahead of the catch-all. Requested #h
  channels intersect accessible_channels (same outcome as the
  catch-all access-scope skip); every returned event passes the
  identical four per-event gates (filters_match,
  reader_authorized_for_event, is_author_only_event,
  event_in_accessible_channel). Top-1-per-channel is guaranteed by
  construction, so the multi-#h limit-budget hazard that rules out
  plain multi-#h pushdown does not apply.
- desktop get_channels: N filters -> 1 filter.

Verified: Postgres-backed equivalence test (bulk winners == serial
per-channel winners, incl. same-second id-ASC tie-break, soft-delete
and wrong-kind exclusion, kinds None/empty semantics); cargo test
-p buzz-relay -p buzz-db green; desktop tauri tests 864 pass; workspace
clippy --all-targets -D warnings clean.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
…tinct-on

* origin/main:
  fix(desktop): simplify workspace rail badges (#1462)
  perf(desktop): instant channel switching — non-blocking first paint, persisted snapshots (#1452)

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
@tlongwell-block tlongwell-block force-pushed the perf/last-message-distinct-on branch from 7baf6a9 to 9ce97bd Compare July 2, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant