import db from '@adonisjs/lucid/services/db' /** files ids per slice: a few seconds of reads from the HDD. */ const SLICE = 100_000 /** * The file_keys filter (LOG.md 2026-10-06 (7)): a 64-bit hash of every * file_key. The insert trigger adds new keys; `fill` copies the keys of * files from before, in id order and a slice at a time, and marks the * filter complete when it reaches the files that existed when it started * (everything after that has been added by the trigger, which was live * first). FileStore uses the filter only once it is complete. */ class FileKeys { private completeSince = 0 /** Whether every file's key is in file_keys; cached once true. */ async complete(): Promise { if (this.completeSince) return true const row = await db.from('file_keys_fill').where('id', 1).first() if (row?.complete) this.completeSince = Date.now() return Boolean(row?.complete) } /** Forgets the cached state (tests). */ forget() { this.completeSince = 0 } /** Copies keys until `budgetMs` has passed or the fill is done; returns the files read and whether it's complete. */ async fill(budgetMs: number): Promise<{ read: number; complete: boolean; nextId: number; untilId: number }> { const started = Date.now() let state = await db.from('file_keys_fill').where('id', 1).first() if (!state) return { read: 0, complete: false, nextId: 0, untilId: 0 } if (state.until_id === null) { // Everything inserted from here on goes through the trigger (live since the migration). const [{ max }] = await db.from('files').max('id as max') await db.from('file_keys_fill').where('id', 1).update({ until_id: Number(max ?? 0), updated_at: new Date() }) state = await db.from('file_keys_fill').where('id', 1).first() } let next = Number(state.next_id) const until = Number(state.until_id) let read = 0 while (next <= until && Date.now() - started < budgetMs) { const to = Math.min(until, next + SLICE - 1) const result = await db.rawQuery( `INSERT INTO file_keys (key_hash) SELECT DISTINCT hashtextextended(file_key, 0) FROM files WHERE id BETWEEN ? AND ? AND file_key IS NOT NULL ON CONFLICT DO NOTHING`, [next, to] ) read += Number(result.rowCount ?? 0) next = to + 1 await db.from('file_keys_fill').where('id', 1).update({ next_id: next, complete: next > until, updated_at: new Date() }) } return { read, complete: next > until, nextId: next, untilId: until } } } export default new FileKeys()