Poster search through a posters table and releases_poster_newest_idx; complete releases are final; the tail before everything (-3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c9a9c0155e
commit
c0e00f1707
8 files changed
+105
-8
No files matched your search
@@ -0,0 +1,24 @@
|
||||
import { BaseSchema } from '@adonisjs/lucid/schema'
|
||||
|
||||
/**
|
||||
* Poster search (LOG.md 2026-10-16 (70)): "poster ILIKE '%…%'" on releases had no index, so it walked all 50M releases
|
||||
* newest first; a poster with few releases took 30+ minutes. posters holds each poster once (3M, many random per file
|
||||
* of an obfuscated upload) with a trigram index; releases_poster_newest_idx gives a poster's releases newest first.
|
||||
* Both by the poster's first 200 characters: a spam poster of 3,024 bytes is over a btree entry's 2,704. The batch store
|
||||
* adds new posters. Homelab built the releases index by hand first (concurrently); a no-op there.
|
||||
*/
|
||||
export default class extends BaseSchema {
|
||||
static disableTransactions = true
|
||||
|
||||
async up() {
|
||||
this.schema.raw(`CREATE TABLE IF NOT EXISTS posters (poster text PRIMARY KEY)`)
|
||||
this.schema.raw(`INSERT INTO posters (poster) SELECT DISTINCT left(poster, 200) FROM releases WHERE poster IS NOT NULL ON CONFLICT DO NOTHING`)
|
||||
this.schema.raw(`CREATE INDEX IF NOT EXISTS posters_trgm ON posters USING gin (poster gin_trgm_ops)`)
|
||||
this.schema.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS releases_poster_newest_idx ON releases (left(poster, 200), posted_at DESC NULLS LAST, id DESC)`)
|
||||
}
|
||||
|
||||
async down() {
|
||||
this.schema.raw(`DROP INDEX CONCURRENTLY IF EXISTS releases_poster_newest_idx`)
|
||||
this.schema.raw(`DROP TABLE IF EXISTS posters`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { BaseSchema } from '@adonisjs/lucid/schema'
|
||||
|
||||
/**
|
||||
* release_final() with the release's complete flag: a release with every file its counter announced is final as it is
|
||||
* (nothing it lacks can come); the time window only decides for the incomplete and those without a counter (LOG.md
|
||||
* 2026-10-16 (70): a complete episode posted an hour before was "pending" for six hours more).
|
||||
*/
|
||||
export default class extends BaseSchema {
|
||||
async up() {
|
||||
// Beside the five-argument one, which the services still running call until the deploy restarts them.
|
||||
this.schema.raw(`
|
||||
CREATE FUNCTION release_final(g integer, first_article bigint, last_article bigint, posted timestamptz, last_posted timestamptz, complete boolean)
|
||||
RETURNS boolean LANGUAGE sql STABLE AS $$
|
||||
SELECT coalesce(complete, false) OR EXISTS (
|
||||
SELECT 1 FROM range_blocks b
|
||||
WHERE b.group_id = g AND b.start_id <= first_article AND b.end_id >= last_article
|
||||
AND (b.at_group_start OR b.posted_low <= posted - interval '6 hours')
|
||||
AND b.posted_high >= coalesce(last_posted, posted) + interval '6 hours')
|
||||
$$`)
|
||||
}
|
||||
|
||||
async down() {}
|
||||
}
|
||||
Reference in new issue
Block a user