import { env } from "cloudflare:workers";
import { all } from "./db";
import { forEachScope } from "./account-scope";
/**
* One-shot data backfills that walk every repository once, a page per cron run, so new indexes can cover rows
* written before they existed. Progress lives in KV (`backfill:<name>`): the last repository id visited, or
* DONE. Readers that need the index consult `backfillDone` and keep their old (slower) path until it is.
*/
const DONE = "done";
const PAGE = 100;
/** Keep paging within one cron run while there is time left (the run is shared with other tasks). */
const BUDGET_MS = 20_000;
const CONCURRENCY = 8;
const MAX_VISITS_PER_RUN = 500;
const finished = new Set<string>();
function key(name: string): string {
return `backfill:${name}`;
}
/** False until the backfill has finished, and whenever its state can't be read (callers keep their safe path). */
export async function backfillDone(name: string): Promise<boolean> {
if (finished.has(name)) return true;
const state = await Promise.resolve()
.then(() => env.KV.get(key(name)))
.catch(() => null);
if (state === DONE) finished.add(name);
return state === DONE;
}
/**
* Visits pages until done, out of time, or out of visits for this run (DO calls count toward the invocation's
* subrequest limit). Visits must be idempotent: a page with a failed visit saves the cursor just before the first
* failure and stops, so the next run retries from there, and DONE is only written once every id was visited.
*/
async function walk(name: string, table: "repos" | "accounts", visit: (id: string) => Promise<void>): Promise<boolean> {
if (await backfillDone(name)) return true;
const started = Date.now();
let visits = 0;