Seed the instruments table in one immediate transaction

`next build` imports lib/db.ts from several workers at once. On a fresh
machine they all saw an empty instruments table and raced to insert the
same symbols, failing the build with "UNIQUE constraint failed:
instruments.symbol". The emptiness check and the inserts now share one
IMMEDIATE transaction, and the insert is OR IGNORE as a backstop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-18 22:21:28 -05:00
co-authored by Claude Opus 5.5
parent d7d1e2cdae
commit 98016b3eb3
+10 -5
View File
@@ -247,12 +247,17 @@ const SYMBOLS = ['NQ','MNQ','ES','MES','YM','MYM','RTY','M2K','GC','MGC','SI','C
// from the Instruments page, they just start off.
const DEFAULT_ENABLED = new Set(['NQ', 'GC', 'CL']);
// Seed instruments table if empty
const instrCount = (db.prepare('SELECT COUNT(*) as count FROM instruments').get() as { count: number }).count;
if (instrCount === 0) {
const ins = db.prepare('INSERT INTO instruments (symbol, enabled) VALUES (?, ?)');
// Seed instruments table if empty. Check and insert share one IMMEDIATE
// transaction: `next build` imports this module from several workers at once,
// and on a fresh machine they all saw an empty table and raced to insert the
// same symbols (UNIQUE constraint failed: instruments.symbol). OR IGNORE is
// the backstop if the lock is ever skipped.
db.transaction(() => {
const instrCount = (db.prepare('SELECT COUNT(*) as count FROM instruments').get() as { count: number }).count;
if (instrCount > 0) return;
const ins = db.prepare('INSERT OR IGNORE INTO instruments (symbol, enabled) VALUES (?, ?)');
for (const s of SYMBOLS) ins.run(s, DEFAULT_ENABLED.has(s) ? 1 : 0);
}
}).immediate();
export interface InstrumentRow {
symbol: string;