f343f4e86e9bff3438f8e519cbf9746e9b6571e1
`app_users.userid` is a `GENERATED BY DEFAULT AS IDENTITY` column. It did not look like one: `information_schema.columns.column_default` is empty for identity columns, and reading that as "no default at all" is how this came to compute its own id with MAX+1. That worked, and quietly did the wrong thing. An explicit id does not advance the sequence, so two allocators ended up running in parallel — the sequence sat at 1447 while MAX(userid) had reached 9189. They cannot collide today, because almost nothing occupies the range between, but they converge on every insert and the first collision would be a primary key violation on a live sign-up. The insert now omits userid and reads it back with RETURNING. The advisory lock stays, because it was never about the id: two supervisors adding staff at the same instant could both find a PIN free and both take it, and a duplicate PIN attributes a bill to whichever row is read first. Also documents why the email columns go through NULLIF. `app_users_email_unique` is real, and a second cashier created without an email would otherwise collide on the empty string — while NULLs do not collide in Postgres. A cashier who signs in by PIN alone has no email, which is the common case rather than the edge one. Verified in a rolled-back transaction against live data: creation allocates 1448, the sequence advances 1447 -> 1448, a second emailless user is accepted, and a duplicate PIN is still refused. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Description
No description provided
Languages
Go
99.4%
JavaScript
0.6%