trajectory
sql-migration-01core-12sqlDifficulty tier 4/5

Backfill a NOT NULL column on a large table without a long lock

postgresmigrationslockingbackfillddl

Task parameters

Reference steps
10
Step ceiling
45
Runs
15
Solved
14 of 15
Models
5

What is broken, and what fixed means

A two file migration adds orders.status, derives it from four existing columns and makes it NOT NULL. It is already written, it applies, and every row ends up with the right value. It was rejected in review because it does all of that in three statements: one UPDATE over the whole table, which on the production table is twenty minutes of row locks and one snapshot held open for all of it, and a bare SET NOT NULL, which takes ACCESS EXCLUSIVE and then scans 180 million rows while holding it. Neither statement bounds how long it will wait for a lock. The task is to get the same column by a route that keeps every statement short: add it nullable, backfill in batches that commit, reach NOT NULL through a CHECK added NOT VALID and then validated, and set lock_timeout and statement_timeout before any of it. The workspace runs its own PostgreSQL 16 cluster with pg_stat_statements loaded, and the hidden suite reads per statement timings out of it rather than trusting the shape of the SQL.

Results by model

One group per model. The solve rate carries its spread across seeds, and every run below it links to the full step by step replay.

stub:hasty

66.7%+/- 47.1% over 3 seeds

3 runs, seeds 0, 1, 2

stub:methodical

100.0%+/- 0.0% over 3 seeds

3 runs, seeds 0, 1, 2

stub:reckless

100.0%+/- 0.0% over 3 seeds

3 runs, seeds 0, 1, 2

stub:sloppy

100.0%+/- 0.0% over 3 seeds

3 runs, seeds 0, 1, 2

stub:thrasher

100.0%+/- 0.0% over 3 seeds

3 runs, seeds 0, 1, 2