mirror of
https://github.com/block/buzz.git
synced 2026-11-08 10:21:46 +00:00
fix(search): match the resolved kind-0 head; judge rewrite by expression
Addresses the two open findings from the review of PR #8044. The third,
the stale migration-version pin, was fixed in c2b3426d6.
Profile search applied `search_tsv @@ query` before `DISTINCT ON (pubkey)`,
so it kept the newest *matching* live row, not the current head. With two
live kind-0 rows for one pubkey, an older `Wes` and a newer `Alice`, a
search for `wes` dropped the current row and returned the superseded one,
exposing a name and `about` text the user had removed. Make the head
subquery the row source instead: it resolves each pubkey's newest live
kind-0 row in the replaceable store's `created_at DESC, id ASC` order with
only the identity fences inside it (community, `deleted_at IS NULL`,
`kind = 0`, authors), and the search predicate, channel scope, and
since/until apply to the resolved head, as they would on a store holding
one row per person. The outer wrapper that carried `created_at` and
`search_tsv` through `heads` goes away because the outer query reads them
from the subquery directly; the artifact-head predicate and `kind = ANY`
are skipped for kind 0 since the subquery pins the kind. Non-kind-0
searches emit the same SQL as before; `push_authors` serves both paths.
`ProfileSearchPolicy::rewrite_pending` required `events_populated`, so a
database that was populated when 0056 ran and emptied afterwards reported
`ok` at relay startup and from `buzz-admin profile-search-policy` while
every new profile still indexed its whole JSON, because 0056 never reruns.
Judge the rewrite on the live expression alone and drop the
`events_populated` field and its query. The relay warning no longer says
"populated", and docs/profile-search-deployment.md records that emptying
the table does not help.
Tests. `kind0_search_resolves_the_current_head_before_matching` seeds a
superseded `Wes` head under a current `Alice` head and asserts that `wes`
finds nobody, `alice` finds the current row, and an `until` window that
excludes the current head finds nobody rather than the stale row. With
the search predicate moved back into the head subquery it fails at the
first assertion (the stale row returns) while the existing duplicate-heads
test still passes. `profile_search_policy_probe_tracks_the_maintenance_
rewrite` gains the sequence populated upgrade, `DELETE FROM events`, probe
still pending, a new avatar-only profile matches `needle`, maintenance
rewrite clears the probe and reindexes that row. The
`empty_table_is_never_pending` unit test goes with the term it covered.
Plan check on a scratch copy of the local database (PostgreSQL 17.9)
seeded with 20k chat rows, 3,000 profiles including 300 with ~50 KB avatar
tsvectors, and 500 duplicate heads: the head subquery is bounded to the
community's kind-0 rows (`idx_events_addressable` on mixed partitions),
3,500 rows sorted in ~1 MB; 1.7 ms against 0.4 ms for the old shape on the
post-0056 expression and 6.7 ms against 4.8 ms on the whole-JSON
expression. On that data the old shape returned the renamed `Wes 7` for
`wes`; the new shape does not.
Verified with the lld linker workaround for sqlx proc-macros on this
machine: `scripts/postgres-test-run.sh -p buzz-search --tests` (24
passed), `scripts/postgres-test-run.sh -p buzz-db --lib -E
'test(migration::postgres_tests)'` (13 passed), `cargo test -p buzz-db
--lib search_policy`, `cargo test -p buzz-admin profile_search`, `cargo
fmt --all --check`, and `cargo clippy -p buzz-search -p buzz-db -p
buzz-admin -p buzz-relay --all-targets -- -D warnings`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
This commit is contained in:
@@ -22,23 +22,25 @@ startup probe can kill and repeat. The query-side name-first ordering in
|
||||
this at every boot until the rewrite has run:
|
||||
|
||||
```text
|
||||
WARN Profile search still indexes whole kind-0 JSON on this populated database; run the maintenance rewrite script=scripts/maintenance/profile_search_text_fields.sql procedure=docs/profile-search-deployment.md
|
||||
WARN Profile search still indexes whole kind-0 JSON on this database; run the maintenance rewrite script=scripts/maintenance/profile_search_text_fields.sql procedure=docs/profile-search-deployment.md
|
||||
```
|
||||
|
||||
## Who needs the rewrite
|
||||
|
||||
Any deployment that held events before starting a relay version with migration
|
||||
0056. Fresh installs do not. Check without side effects, from a shell with the
|
||||
relay's `DATABASE_URL`:
|
||||
0056. Fresh installs do not. Emptying `events` later does not help: the
|
||||
migration has already run, so the old expression stays and every profile
|
||||
published afterwards indexes its whole JSON again. Check without side effects,
|
||||
from a shell with the relay's `DATABASE_URL`:
|
||||
|
||||
```sh
|
||||
buzz-admin profile-search-policy
|
||||
```
|
||||
|
||||
The command opens one read-only session and prints the live expression,
|
||||
whether `events` has rows, and `outcome`: `ok` (exit 0), `rewrite_pending`
|
||||
(exit 2), or `missing` (exit 5, the column does not exist yet). The same probe
|
||||
drives the startup warning, so the two never disagree.
|
||||
The command opens one read-only session and prints the live expression and
|
||||
`outcome`: `ok` (exit 0), `rewrite_pending` (exit 2), or `missing` (exit 5,
|
||||
the column does not exist yet). The same probe drives the startup warning, so
|
||||
the two never disagree.
|
||||
|
||||
The equivalent SQL:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user