- Shipped
- August 11, 2026 at 6:10 PM UTC
- Author
- Kamo
- Commit
- b458e58
Three runners: BillingGroupRosterMigration creates billing_pay_group_members — whom a group means to pay for, before it has bought anything. org_id is denormalised onto it so the database can enforce "a member is on at most one active billing group per organization" as a partial unique. Two groups paying for one person is two charges for one seat, and nothing else in the schema would catch it: SubscriptionMember's uniques are per-subscription and AccountLicense has none. **************** adds billing_group_uid to account_subscriptions and replaces the three-column unique with two partial uniques. Widening it to four columns would not work — NULLs are distinct inside a unique index here, so every non-group row would stop being constrained. BillingPayGroupColumnsMigration adds market_id, seat_limit and status, and marks existing groups LEGACY. Those were billed to whatever account uid the request body carried, and the console sent the organization's, so their account_uid is almost certainly wrong. There is no safe way to guess which card was meant, and guessing wrong bills the wrong person, so they are refused at the charge point instead of repaired. The first run showed why the retry matters: both replacement indexes failed with 40001 could not serialize access, the runner logged "skipped" and carried on, and the DROP of the old constraint then succeeded — leaving account_subscriptions with no uniqueness at all, which is strictly worse than the collision this fixes. DDL statements now retry on serialization failure, and the drop is skipped entirely unless both replacements are confirmed in place.