From 6fde4b73cd57e2e4ff8a9a739a60f76ac0d64280 Mon Sep 17 00:00:00 2001 From: rays Date: Thu, 15 Oct 2026 15:27:25 +0000 Subject: [PATCH] Currently Listening and Pinned start from the person's own rows (#249) coalesce(s.position, 0) > 5 and coalesce(s.flagged, false) hid from Postgres that a match needs a row of entry_state, so it kept the outer join and scanned every item: Currently Listening read all 574,000 (74,733 blocks from disk), 970 ms of its 1.3 s. s.position > 5 and s.flagged mean the same, a missing row being neither, and let it start from the person's own rows: 2.2 ms, and Pinned 32 ms to 2.3 (EXPLAIN ANALYZE, BUFFERS on production, read only). Unread keeps its coalesce: no row is unread. Co-Authored-By: Claude Opus 5.5 --- crates/ipx/CHANGELOG.md | 2 ++ crates/ipx/src/db.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/ipx/CHANGELOG.md b/crates/ipx/CHANGELOG.md index 6874c54..c043fc6 100644 --- a/crates/ipx/CHANGELOG.md +++ b/crates/ipx/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Feeds are fetched accepting brotli and zstd as well as gzip (#244). - On Postgres the daemon keeps all twenty of its database connections open, so a page load no longer waits for new ones, 80-330 ms each: the feed list and item lists took 0.3-1.6 s now and then, where their queries take 5-30 ms (#241). +- Currently Listening and the Pinned filter answer in a few milliseconds: they read every item on the server, 1.3 s for Currently Listening (#249). + ### Added - The page's report of how long a list took to show after a click (`list_shown`, with the view and filter) is logged under that click's trace (#246). diff --git a/crates/ipx/src/db.rs b/crates/ipx/src/db.rs index da8ed0d..3d1c4a1 100644 --- a/crates/ipx/src/db.rs +++ b/crates/ipx/src/db.rs @@ -1219,14 +1219,18 @@ impl Filter { // true and false, not 1 and 0: the columns are booleans on Postgres, and SQLite // reads true and false as 1 and 0. Self::All => "true", + // An item with no row of state is unread, so this one keeps its coalesce. Self::Unread => "NOT coalesce(s.read, false)", - Self::Flagged => "coalesce(s.flagged, false)", + // Pinned and in progress need a row of state, said without coalesce so the database + // can tell, and starts from this person's few rows instead of every item: Currently + // Listening read all 574,000, 970 ms, where this takes 2 (#249). + Self::Flagged => "s.flagged", Self::Downloaded => { "EXISTS (SELECT 1 FROM enclosures x WHERE x.feed_id = e.feed_id AND x.guid = e.guid AND x.path IS NOT NULL)" } Self::InProgress => { - "coalesce(s.position, 0) > 5 + "s.position > 5 AND (coalesce(s.duration, e.duration, 0) = 0 OR s.position * 10 < coalesce(s.duration, e.duration) * 9)" }