Skip to content
FeaturesPricingAffiliateBlogHelpAboutContact
Get StartedSign In
Back to Blog
industry2026-05-2312 min read

I took a phone order, the kitchen tablet showed an empty ticket — D1 batch atomicity and a parent+child split in the admin order POST

Hannah (35) runs Linnenklee wine bar+bistro in Berlin Kreuzberg. Friday 19:42 a phone customer orders 3 plates of Königsberger Klopse + 2 Soljanka + 4 Berliner Weisse. Hannah types it into the admin tablet — Order #4892 confirmed, €54.60. Two minutes later her chef Mads: "Ticket 4892 has no items, what am I supposed to cook?" KDS shows order header but empty items list. Hannah tries three times, three empty tickets. thMenu support forensic: 3 wrong theories (session timeout, FK violation, body items missing) busted. D1 query log shows TWO separate transactions: `db.prepare(INSERT orders).run()` committed → `db.batch(itemStmts)` hit transient "database is locked" → outer try/catch handler returned 500 but parent order row had already committed in D1's per-statement atomic model (no implicit rollback). Customer-side order POST already had unified `db.batch([orderStmt, ...itemStmts])` via PR #619 FFF-prelim — admin-side sibling endpoint hardening parity gap. **PR #660 batch X F1** fix: `apps/web-admin/src/app/api/orders/route.ts:348-380` unified into single `db.batch([orderInsertStmt, ...itemStmts])` call + items.length ≤ 100 cap (parity with customer + kiosk endpoints). Pattern: parent+child writes in D1 MUST share a single batch — D1 has no cross-statement transactions; two separate round-trips = partial-write intermediate state possible.

th

thMenu Team

thmenu.com

Found this helpful? Share it.