The directory's public pages read the list once a minute (#341)

/, /all, /search and /category are anyone's to ask for (#215), and each
called Store::list: the whole catalogue, joined with the snapshots and
grouped, on the 10-connection pool the refresher and every server's
/v1 calls share, on the Postgres that holds production ipx's database.
The pages' Cache-Control did nothing for a request with a query string
of its own, such as /search?q=<random>.

Directory::pages_list keeps the list for a minute behind a mutex held
through the read, so a burst of misses waits on one query rather than
each sending its own. Filtering and rendering still happen per request,
in memory. /v1/feeds, behind a token, still reads the database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 14:06:19 +00:00
parent 80f12cda70
commit fc04a741a6
3 changed files with 44 additions and 6 deletions

View File

@@ -58,17 +58,17 @@ impl Filter {
}
async fn home(State(dir): State<Shared>) -> Response {
let all = match dir.store.list().await {
let all = match dir.pages_list().await {
Ok(a) => a,
Err(e) => return failed(e),
};
let mut top = all.clone();
let mut top = (*all).clone();
top.sort_by(|a, b| {
b.subscribers.cmp(&a.subscribers).then_with(|| name(a).to_lowercase().cmp(&name(b).to_lowercase()))
});
top.truncate(10);
let mut cats: BTreeMap<&str, Vec<&Listing>> = BTreeMap::new();
for l in &all {
for l in all.iter() {
cats.entry(l.category.as_deref().unwrap_or("Other")).or_default().push(l);
}
let mut b = String::new();
@@ -123,8 +123,8 @@ async fn category(State(dir): State<Shared>, Path(c): Path<String>, Query(f): Qu
/// A page of tiles: those `pick` takes, of the kind and subcategory asked for, A to Z.
async fn listed(dir: &Shared, title: &str, here: &str, f: &Filter, pick: impl Fn(&Listing) -> bool) -> Response {
let mut ls: Vec<Listing> = match dir.store.list().await {
Ok(a) => a.into_iter().filter(|l| pick(l)).collect(),
let mut ls: Vec<Listing> = match dir.pages_list().await {
Ok(a) => a.iter().filter(|l| pick(l)).cloned().collect(),
Err(e) => return failed(e),
};
if ls.is_empty() && here.starts_with("/category/") {