From aa430f6d500f30de2fb60944e2c1809fbd8e7a8e Mon Sep 17 00:00:00 2001 From: rays Date: Fri, 23 Oct 2026 15:24:29 +0000 Subject: [PATCH] A page's files are looked up by feed and item, not item alone (#344) entries_where found a page's enclosures with guid IN (...). The only index is enclosures_entry (feed_id, guid), so Postgres read the whole of it: 587k rows, 9,150 buffers and 250 ms for user 2's 50 newest, on every /api/entries, feed page, Signal page and single item. With the page's feed ids as well it is an index lookup, 0.5 ms. Co-Authored-By: Claude Opus 5.5 --- crates/ipx/CHANGELOG.md | 1 + crates/ipx/src/db.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/crates/ipx/CHANGELOG.md b/crates/ipx/CHANGELOG.md index 8ead064..9cbde91 100644 --- a/crates/ipx/CHANGELOG.md +++ b/crates/ipx/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Traces show more of where a request's time goes: a page of items' query and its files apart, artwork's cache, check and fetch, the status counts, and each part of changing or leaving a feed (#350). +- A page of items finds its files by feed as well as item: it read every file's row in the database, about a quarter of a second a page (#344). ### Fixed diff --git a/crates/ipx/src/db.rs b/crates/ipx/src/db.rs index b441a73..b999bdf 100644 --- a/crates/ipx/src/db.rs +++ b/crates/ipx/src/db.rs @@ -1498,8 +1498,12 @@ impl Db { // Only the guids on this page, so a feed with thousands of entries stays cheap. A page // can span feeds, so each file is matched to its row by feed as well as guid, below. + // The page's feeds too: the index is (feed_id, guid), and by guid alone Postgres read + // all of it, 587k rows and 250 ms for a page of 50 (#344). + let feeds: std::collections::BTreeSet<&str> = rows.iter().map(|r| r.feed_id.as_str()).collect(); let encs = tracing::Instrument::instrument( enclosures::Entity::find() + .filter(enclosures::Column::FeedId.is_in(feeds.into_iter().map(str::to_owned))) .filter(enclosures::Column::Guid.is_in(rows.iter().map(|r| r.guid.clone()))) .order_by_asc(enclosures::Column::Id) .all(&self.orm),