Load the feed list in five queries, not six per feed (#94)
GET /api/feeds called feed_summary, http_state, blocklist and unread_count for every feed: about 950 round trips to Postgres for 160 feeds, 320 ms on every page load. Db::feed_list asks for the feed rows, entry counts, download counts, the person's unread counts and block lists once each, and the handler reads from that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
13
src/web.rs
13
src/web.rs
@@ -744,6 +744,7 @@ async fn feeds(
|
||||
.collect();
|
||||
let counts = state.ctx.db.subscriber_counts().await?;
|
||||
let pinned = state.ctx.db.pinned_feeds(user.id).await?;
|
||||
let mut listed = state.ctx.db.feed_list(user.id).await?;
|
||||
let mut out = Vec::with_capacity(mine.len());
|
||||
for sub in &subs {
|
||||
let (id, feed) = (&sub.id, &sub.cfg);
|
||||
@@ -751,8 +752,8 @@ async fn feeds(
|
||||
// the same fallback the scanner uses (`Db::subscribers`).
|
||||
let up = feed.group.as_deref().and_then(|g| mine.get(g));
|
||||
let Some(mine) = mine.get(id) else { continue };
|
||||
let s = state.ctx.db.feed_summary(id).await?;
|
||||
let st = state.ctx.db.http_state(id).await?;
|
||||
// A feed not scanned yet has no row: blank, as feed_summary gave it.
|
||||
let crate::db::FeedListing { summary: s, ttl_mins, blocked, unread } = listed.remove(id).unwrap_or_default();
|
||||
out.push(FeedRow {
|
||||
id: id.clone(),
|
||||
url: feed.url.clone(),
|
||||
@@ -766,7 +767,7 @@ async fn feeds(
|
||||
.clone()
|
||||
.or_else(|| up.and_then(|u| u.keywords.clone()))
|
||||
.unwrap_or_else(|| feed.keywords.clone()),
|
||||
blocked: state.ctx.db.blocklist(user.id, id).await?,
|
||||
blocked,
|
||||
allow_explicit: mine
|
||||
.allow_explicit
|
||||
.or(up.and_then(|u| u.allow_explicit))
|
||||
@@ -789,11 +790,11 @@ async fn feeds(
|
||||
.schedule
|
||||
.as_deref()
|
||||
.and_then(crate::config::parse_interval),
|
||||
every_mins: crate::due_after(&cfg, feed, st.ttl_mins) / 60,
|
||||
every_mins: crate::due_after(&cfg, feed, ttl_mins) / 60,
|
||||
last_checked: s.last_checked,
|
||||
next_check: s
|
||||
.last_checked
|
||||
.map(|t| t + crate::due_after(&cfg, feed, st.ttl_mins) as i64),
|
||||
.map(|t| t + crate::due_after(&cfg, feed, ttl_mins) as i64),
|
||||
failing: s
|
||||
.error_since
|
||||
.filter(|since| crate::db::now() - since >= FLAG_AFTER_SECS)
|
||||
@@ -803,7 +804,7 @@ async fn feeds(
|
||||
last_error: s.last_error,
|
||||
entries: s.entries,
|
||||
downloaded: s.downloaded,
|
||||
unread: state.ctx.db.unread_count(user.id, id).await?,
|
||||
unread,
|
||||
subscribers: counts.get(id).copied().unwrap_or(0),
|
||||
pinned: pinned.contains(id),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user