/api/art asked whether a feed or item names the image before anything else, cache hits included, and entries.image had no index: a sequential scan of entries, 571 ms on production, 50 at once for a page of images. A kept image is served first now, since it was only kept once let through, and entries.image has an index: hash on Postgres, whose btree refuses keys over about 2.7 KB. The Directory lists podcasts nobody here subscribes to, and their artwork was refused, so the page drew initials. ipx remembers the images the directory's listings and pages carry and lets those through too; any other address is still refused, so /api/art cannot be used to fetch arbitrary URLs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -126,6 +126,15 @@ async fn create_missing(orm: &sea_orm::DatabaseConnection) -> Result<()> {
|
||||
// An expression index has no statistics until its table is analysed, and without them
|
||||
// Postgres guessed a third of entries were recent and read them all, entries_recent unused
|
||||
// (#274). Analysed once when they are missing; autovacuum keeps them after.
|
||||
// `/api/art` asks whether a feed or item names an image before serving it, and without an
|
||||
// index that read all of entries, 571 ms an image, 50 at once on a page (#272). A hash index
|
||||
// on Postgres, whose btree refuses a key over about 2.7 KB, longer than some image addresses.
|
||||
orm.execute_unprepared(match orm.get_database_backend() {
|
||||
sea_orm::DbBackend::Postgres => "CREATE INDEX IF NOT EXISTS entries_by_image ON entries USING hash (image)",
|
||||
_ => "CREATE INDEX IF NOT EXISTS entries_by_image ON entries (image)",
|
||||
})
|
||||
.await
|
||||
.context("indexing entries.image")?;
|
||||
if orm.get_database_backend() == sea_orm::DbBackend::Postgres {
|
||||
let has = Statement::from_string(
|
||||
sea_orm::DbBackend::Postgres,
|
||||
|
||||
Reference in New Issue
Block a user