Skip to content

fix(postgres-migration): make the seed re-runnable - #99

Merged
sishuo-yang merged 1 commit into
mainfrom
sishuo-yang/postgres-migration-rerunnable-seed
Aug 21, 2026
Merged

sishuo-yang merged 1 commit into
mainfrom
sishuo-yang/postgres-migration-rerunnable-seed

Conversation

@sishuo-yang

Copy link
Copy Markdown
Collaborator

Follow-up to #98, which is already merged. Same workshop, unrelated defect, found while running module 01 end to end on a live RDS stack.

The problem

A seed that died partway could not be restarted. The second run re-inserted products, hit sku's unique constraint and aborted, so recovering meant manual cleanup before the file would run at all -- and nothing in the file hinted at that.

This is not hypothetical. It is exactly what a killed or interrupted seed leaves you holding, and the seed is a 29-minute unattended job that participants are told to start early and walk away from.

The fix

Each INSERT now carries a count guard against its own target:

FROM generate_series(1, :customers) g
WHERE (SELECT count(*) FROM customers) < :customers;

Each table is loaded by one statement, so a table is either empty of seed rows or complete -- there is no partially-seeded state for the guard to misjudge.

The orders and order_items guards compare against the target rather than testing for emptiness, which matters: app/writer.py inserts into both, so an emptiness test would see the writer's rows and conclude the seed was finished while its 12M orders and 48M items were still missing.

The header now documents that the file is re-runnable, and gives the TRUNCATE ... RESTART IDENTITY line for the separate case of reseeding at a different size.

Verification

Exercised in four states, on both a local Postgres 17 container and the live RDS instance:

State Expected Result
Fresh load full insert 500 / 100 / 2000 / 8000
Re-run, fully seeded nothing INSERT 0 0 x4
order_items lost, rest intact reload only order_items 0 / 0 / 0 / 8000
Writer rows present in orders still treated as complete INSERT 0 0 x4

Against the already-seeded RDS instance a re-run reports four INSERT 0 0 and completes in 20 seconds. Before this change that run aborted.

The playbook half of this workshop's fixes is in WorkshopHouse#60.

🤖 Generated with Claude Code

A seed that died partway could not be restarted. The second run re-inserted
products, hit sku's unique constraint and aborted, so recovering meant manual
cleanup before the file would run at all -- and the file gives no hint of that.

Guard each INSERT with a count against its own target. Each table is loaded by
one statement, so a table is either empty of seed rows or complete and there is
no partially-seeded state for the guard to misjudge. The orders and order_items
guards compare against the target rather than testing for emptiness, because
app/writer.py inserts into both and would otherwise make them look finished
while the seed rows are still missing.

Verified in four states: fresh load, re-run when fully seeded, re-run after
losing only order_items, and re-run with writer rows present. Re-running against
an already-seeded RDS instance now reports four INSERT 0 0 and takes 20 seconds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sishuo-yang
sishuo-yang merged commit 7bdf993 into main Aug 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant