Skip to content

Commit 9f67206

Browse files
fix(tables): chunk backfill order-key writes
A single UPDATE … FROM (VALUES …) over a whole large table overflows the JS call stack while drizzle assembles the VALUES list (and would blow past Postgres's 65535 bound-param limit at ~32k rows) — large tables failed with 'Maximum call stack size exceeded'. Write in 1000-row chunks inside the same per-table transaction so keying stays atomic.
1 parent 172ebb4 commit 9f67206

1 file changed

Lines changed: 22 additions & 11 deletions

File tree

apps/sim/scripts/backfill-table-order-keys.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ import { drizzle } from 'drizzle-orm/postgres-js'
2727
import postgres from 'postgres'
2828
import { nKeysBetween } from '@/lib/table/order-key'
2929

30+
/**
31+
* Rows written per `UPDATE … FROM (VALUES …)`. One statement for a whole large table builds an
32+
* enormous VALUES list that overflows the JS call stack while drizzle assembles it (and would
33+
* exceed Postgres's 65535-bound-param limit at ~32k rows). Chunks share the one per-table
34+
* transaction, so the table is still keyed atomically.
35+
*/
36+
const WRITE_CHUNK_SIZE = 1000
37+
3038
export async function runBackfill(): Promise<void> {
3139
const dryRun = process.argv.includes('--dry-run')
3240
const connectionString = process.env.DATABASE_URL ?? process.env.POSTGRES_URL
@@ -75,17 +83,20 @@ export async function runBackfill(): Promise<void> {
7583
const keys = nKeysBetween(null, null, rows.length)
7684
if (dryRun) return rows.length
7785

78-
// One UPDATE … FROM (VALUES …) mapping id → key.
79-
const values = sql.join(
80-
rows.map((r, i) => sql`(${r.id}, ${keys[i]})`),
81-
sql`, `
82-
)
83-
await trx.execute(sql`
84-
UPDATE user_table_rows AS t
85-
SET order_key = v.order_key
86-
FROM (VALUES ${values}) AS v(id, order_key)
87-
WHERE t.id = v.id AND t.table_id = ${tableId}
88-
`)
86+
// Chunked UPDATE … FROM (VALUES …) mapping id → key (see WRITE_CHUNK_SIZE).
87+
for (let start = 0; start < rows.length; start += WRITE_CHUNK_SIZE) {
88+
const chunk = rows.slice(start, start + WRITE_CHUNK_SIZE)
89+
const values = sql.join(
90+
chunk.map((r, i) => sql`(${r.id}, ${keys[start + i]})`),
91+
sql`, `
92+
)
93+
await trx.execute(sql`
94+
UPDATE user_table_rows AS t
95+
SET order_key = v.order_key
96+
FROM (VALUES ${values}) AS v(id, order_key)
97+
WHERE t.id = v.id AND t.table_id = ${tableId}
98+
`)
99+
}
89100
return rows.length
90101
})
91102
stats.tablesKeyed += 1

0 commit comments

Comments
 (0)