/** * Builds the composite index SiteEvent_sessionId_timestamp_idx on the * SiteEvent table using CREATE INDEX CONCURRENTLY. * * Must connect via the non-pooler (direct) Neon URL because: * - CREATE INDEX CONCURRENTLY cannot run inside a transaction * - The pooler enforces statement_timeout that overrides SET * * Usage: * DIRECT_URL="postgres://..." node scripts/build-sessionid-index.mjs * * Never hardcode credentials here. Supply DIRECT_URL from the shell only. */ import pkg from "pg"; const { Client } = pkg; const INDEX_NAME = "SiteEvent_sessionId_timestamp_idx"; const directUrl = process.env.DIRECT_URL; if (!directUrl) { console.error("[build-index] DIRECT_URL is not set. Aborting."); process.exit(1); } const client = new Client({ connectionString: directUrl }); async function run() { console.log("[build-index] Connecting via DIRECT_URL..."); await client.connect(); console.log("[build-index] Connected."); console.log("[build-index] SET statement_timeout = 0"); await client.query("SET statement_timeout = 0;"); console.log("[build-index] SET lock_timeout = 0"); await client.query("SET lock_timeout = 0;"); console.log(`[build-index] DROP INDEX IF EXISTS "${INDEX_NAME}"`); await client.query(`DROP INDEX IF EXISTS "${INDEX_NAME}";`); console.log("[build-index] Drop complete (or index did not exist)."); console.log(`[build-index] CREATE INDEX CONCURRENTLY "${INDEX_NAME}" -- this may take several minutes on large tables`); await client.query( `CREATE INDEX CONCURRENTLY "${INDEX_NAME}" ON "public"."SiteEvent" ("sessionId", "timestamp");`, ); console.log("[build-index] Index build complete."); const res = await client.query( `SELECT indisvalid FROM pg_index JOIN pg_class ON pg_class.oid = pg_index.indexrelid WHERE pg_class.relname = $1;`, [INDEX_NAME], ); if (res.rows.length === 0) { console.error("[build-index] Index not found after creation -- something went wrong."); process.exit(1); } const { indisvalid } = res.rows[0]; if (indisvalid) { console.log("[build-index] indisvalid = true -- index is VALID and ready."); } else { console.error("[build-index] indisvalid = false -- index is INVALID. Manual cleanup required."); process.exit(1); } } run() .catch((err) => { console.error("[build-index] Fatal error:", err.message); process.exit(1); }) .finally(() => { client.end().catch(() => {}); });