Make post-commit org setup actually write, and heal the orgs it missed

FixSecurityService
Shipped
August 23, 2026 at 2:45 AM UTC
Author
Kamo
Commit
748ae48

A @Transactional method called from **************** does not open a transaction of its own. Spring fires those callbacks from processCommit BEFORE cleanupAfterCompletion unbinds the thread's transactional resources, so the just-committed transaction is still active and default REQUIRED propagation simply participates in it — and a participant is never committed again. Everything the method writes is discarded when the EntityManager closes, with no exception and no log line. 3903267 moved billing-seat provisioning and the System User TeamMember backfill out of createOrganization's transaction into afterCommit, under a comment saying they now ran "in their own transactions". Neither was changed to REQUIRES_NEW, so from that day both wrote nothing: - Every org created since has no account_subscriptions row at all. Its owner resolves to MemberCapabilities.unlicensed() — no email, no meetings, no calls, no attachments — and cannot recover by signing in, because maybeStartTrialOnLogin starts the clock on a PENDING trial that was never created. The only escape was the owner happening to open Settings → Plans & Billing, whose bootstrap-billing endpoint is documented as a fallback for orgs that pre-dated billing. - No org got the System User membership POST /enter-as requires, so entering one as System Member answers "System User membership missing in target org; backfill pending". This half hid for two weeks because DataLoader re-runs a full System User backfill on every boot; only an org created between two restarts ever showed it. ensureSystemUser, ensureTeamMemberForOrg and the id-based setupForSubOrgCreation are now REQUIRES_NEW. The Organization overload keeps REQUIRED — its caller (AccountController) is an ordinary request path that should share the caller's transaction. **************** scans every afterCommit body in the service, resolves each field.method(...) call to the callee's source, and fails on @Transactional without REQUIRES_NEW. @Async and CompletableFuture.runAsync are exempt: a fresh thread does get a fresh transaction, which is why the email-seed and DNS-provision hooks were never affected. BillingBackfillService heals the orgs already left without a subscription, on boot, the way the System User backfill does. It restores exactly what creation would have written — a PENDING trial — so the 3-day clock still starts on the owner's first real login and nobody loses evaluation time they never got to use. Each org is healed by its own REQUIRES_NEW call, so one bad org costs only itself instead of discarding the whole pass the way ensureTeamMembersForAllOrgs would.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing