import { BaseSchema } from '@adonisjs/lucid/schema' /** * Final completeness by time window (LOG.md 2026-10-16 (67)). A release settles once quiet, but history is read * newest first and in islands: one next to an unread range was "incomplete" twice as often (52.5% against 25.7%), * its missing files only not read yet. An upload is posted within hours, so a release is final (its complete verdict * definite) once done ranges cover its posting times and FINAL_MARGIN either side. * - ranges.posted_low/high: the posting time span the workers report per batch (postedSpan: without the earliest and * latest 1%), the min and max over the range's batches. * - range_blocks: runs of consecutive done ranges per group with their span (RangeBlocks rebuilds it); at_group_start * when the run starts at the group's first range (nothing older to wait for). * - release_final(): whether one block holds the release's articles and reaches FINAL_MARGIN past its posting times. * Computed when asked, so no release row is written (50M updates would double the table on the NVMe). */ export default class extends BaseSchema { async up() { this.schema.raw(`ALTER TABLE ranges ADD COLUMN IF NOT EXISTS posted_low timestamptz, ADD COLUMN IF NOT EXISTS posted_high timestamptz`) this.schema.raw(` CREATE TABLE range_blocks ( group_id integer NOT NULL, start_id bigint NOT NULL, end_id bigint NOT NULL, posted_low timestamptz, posted_high timestamptz, at_group_start boolean NOT NULL, PRIMARY KEY (group_id, start_id) )`) // FINAL_MARGIN: 6 hours. this.schema.raw(` CREATE FUNCTION release_final(g integer, first_article bigint, last_article bigint, posted timestamptz, last_posted timestamptz) RETURNS boolean LANGUAGE sql STABLE AS $$ SELECT 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() { this.schema.raw(`DROP FUNCTION IF EXISTS release_final(integer, bigint, bigint, timestamptz, timestamptz)`) this.schema.raw(`DROP TABLE IF EXISTS range_blocks`) this.schema.raw(`ALTER TABLE ranges DROP COLUMN IF EXISTS posted_low, DROP COLUMN IF EXISTS posted_high`) } }