mirror of
https://github.com/block/buzz.git
synced 2026-11-07 18:01:06 +00:00
perf(db): keep #p reads on the page-then-check path
event_mentions has no author column, so no index can skip foreign author-only rows that mention the reader. Filtering them before LIMIT made a #p:[self] read walk every such row (2.6 s with 120k rows, which any member can write). #p reads now keep the bounded per-event check. Signed-off-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -125,9 +125,10 @@ pub struct EventQuery {
|
||||
/// defense-in-depth catches any residual mismatch.
|
||||
pub shared_gated_reader: Option<Vec<u8>>,
|
||||
/// Author-only visibility reader for [`query_events`]: exclude foreign
|
||||
/// [`AUTHOR_ONLY_KINDS`] before ordering, offset and limit. The SQL
|
||||
/// `count_events` path ignores it, so callers must keep their existing
|
||||
/// fallback gate; COUNT fallbacks built on `query_events` do apply it.
|
||||
/// [`AUTHOR_ONLY_KINDS`] before ordering, offset and limit. Ignored for
|
||||
/// `#p` reads (see `build_query_events_sql`) and by the SQL
|
||||
/// `count_events` path, so callers must keep their per-event check and
|
||||
/// existing fallback gate; COUNT fallbacks built on `query_events` apply it.
|
||||
pub author_only_reader: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
@@ -681,7 +682,15 @@ fn build_query_events_sql(q: &EventQuery) -> QueryBuilder<sqlx::Postgres> {
|
||||
}
|
||||
|
||||
let kinds = q.kinds.as_deref().filter(|k| !k.is_empty());
|
||||
match (kinds, q.author_only_reader.as_ref()) {
|
||||
// A `#p` read is driven by `event_mentions`, which has no author column,
|
||||
// so no index can skip foreign private rows that mention the reader.
|
||||
// Filtering them before LIMIT would read every such row (anyone can write
|
||||
// them), so `#p` reads keep the bounded page-then-check path instead.
|
||||
let author_only_reader = q
|
||||
.author_only_reader
|
||||
.as_ref()
|
||||
.filter(|_| q.p_tag_hex.is_none());
|
||||
match (kinds, author_only_reader) {
|
||||
// Author-only visibility belongs before pagination, like shared-gated
|
||||
// visibility; relay result checks remain defense in depth. With
|
||||
// explicit kinds, split them so Postgres sees `public kinds OR the
|
||||
@@ -2664,6 +2673,14 @@ mod e_tag_filter_shape_tests {
|
||||
kindless.contains("AND (kind NOT IN ($2, $3, $4) OR pubkey = $5)"),
|
||||
"{kindless}"
|
||||
);
|
||||
|
||||
let mut mentions = EventQuery::for_community(CommunityId::from_uuid(uuid::Uuid::nil()));
|
||||
mentions.kinds = Some(vec![30300, 1]);
|
||||
mentions.p_tag_hex = Some("ab".repeat(32));
|
||||
mentions.author_only_reader = Some(vec![1; 32]);
|
||||
let mentions = build_query_events_sql(&mentions).sql().as_str().to_owned();
|
||||
assert!(!mentions.contains("pubkey = $"), "{mentions}");
|
||||
assert!(!mentions.contains("NOT IN"), "{mentions}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user