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 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 15:24:29 +00:00
parent df643536c0
commit aa430f6d50
2 changed files with 5 additions and 0 deletions

View File

@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### 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). - 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 ### Fixed

View File

@@ -1498,8 +1498,12 @@ impl Db {
// Only the guids on this page, so a feed with thousands of entries stays cheap. A page // 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. // 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( let encs = tracing::Instrument::instrument(
enclosures::Entity::find() 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()))) .filter(enclosures::Column::Guid.is_in(rows.iter().map(|r| r.guid.clone())))
.order_by_asc(enclosures::Column::Id) .order_by_asc(enclosures::Column::Id)
.all(&self.orm), .all(&self.orm),