import { BaseSchema } from '@adonisjs/lucid/schema' /** * The pending ladder by class (REBUILD.md decision f; LOG.md (130)): a pending release's class (lib/release/classes.ts: * size bucket × name shape × partial, 1..70; 0 for rows from before) and the bytes of its first target, so the * claim takes the classes with the best record first and the cheapest article within one; pending_classes per * (tool, group, class) how many climbed to the end and how many got a name, `waiting` how many wait (kept by the * batch store, the claim and the discard; recounted by pending:classes), `dropped` as pending_signatures. * The SQL pending_class() is the TypeScript's pendingClass, for the batch store's insert and the backfill. */ export default class extends BaseSchema { static disableTransactions = true async up() { this.schema.raw(` CREATE OR REPLACE FUNCTION pending_class(name text, bytes bigint, partial boolean) RETURNS smallint LANGUAGE sql IMMUTABLE AS $$ SELECT ((CASE WHEN bytes < 1000000 THEN 0 WHEN bytes < 10000000 THEN 1 WHEN bytes < 100000000 THEN 2 WHEN bytes < 1000000000 THEN 3 ELSE 4 END) * 14 + (CASE WHEN s ~ '^[0-9a-fA-F]{16,}$' THEN 1 WHEN s ~ '^[0-9]{6,}$' THEN 2 WHEN s ~ '^[a-z2-7]{20,}$' AND s ~ '[2-7]' THEN 3 WHEN s ~ '^[A-Za-z]+$' THEN 4 WHEN s ~ '^[A-Za-z0-9]+$' THEN 5 WHEN s ~ '^[A-Za-z0-9._-]+$' THEN 6 ELSE 7 END - 1) * 2 + (CASE WHEN partial THEN 1 ELSE 0 END) + 1)::smallint FROM (SELECT regexp_replace(name, '\\.[A-Za-z0-9]{1,4}$', '') AS s) x $$`) this.schema.raw( `ALTER TABLE pending_releases ADD COLUMN IF NOT EXISTS klass smallint NOT NULL DEFAULT 0` ) this.schema.raw(`ALTER TABLE pending_releases ADD COLUMN IF NOT EXISTS target_bytes bigint`) this.schema.raw(` CREATE TABLE IF NOT EXISTS pending_classes ( tool varchar(16) NOT NULL, group_id integer NOT NULL, klass smallint NOT NULL, probed integer NOT NULL DEFAULT 0, named integer NOT NULL DEFAULT 0, waiting integer NOT NULL DEFAULT 0, dropped boolean NOT NULL DEFAULT false, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (tool, group_id, klass) )`) // The claim by class: the cheapest target first within one, the oldest among equals. this.schema.raw( `CREATE INDEX CONCURRENTLY IF NOT EXISTS pending_releases_class_idx ON pending_releases (tool, group_id, klass, target_bytes, created_at) WHERE status = 0` ) } async down() { this.schema.raw(`DROP INDEX CONCURRENTLY IF EXISTS pending_releases_class_idx`) this.schema.raw(`DROP TABLE IF EXISTS pending_classes`) this.schema.raw(`ALTER TABLE pending_releases DROP COLUMN IF EXISTS target_bytes`) this.schema.raw(`ALTER TABLE pending_releases DROP COLUMN IF EXISTS klass`) this.schema.raw(`DROP FUNCTION IF EXISTS pending_class(text, bigint, boolean)`) } }