Commit the processed flag with the money write, not ahead of it

FixBillingService
Shipped
August 26, 2026 at 1:59 AM UTC
Author
Kamo
Commit
2945f3d

markProcessed was REQUIRES_NEW, so it committed processed=true while the handler's money writes were still uncommitted in the suspended outer transaction. JpaTransactionManager does not flush a suspended persistence context, so the inner commit landed ahead of the outer's pending SQL. A commit-phase failure -- AccountInvoice's unique constraint on stripe_invoice_id, its NOT NULL FK on account_uid, or a swallowed IllegalArgumentException in SubscriptionMemberService.add poisoning the shared transaction -- then rolled the money write back and left the event marked done. Stripe's retry skipped it, so the write was LOST, and it did not even reach the poison-event query because processed was true. markProcessed now joins the outer transaction, so a rollback takes the flag with it and the row stays claimed-but-unprocessed, which is what **************** exists to surface. claim and markFailed keep REQUIRES_NEW: the claim must survive a rollback or every retry looks like a first delivery, and the error detail must survive the rollback it describes. Also strengthens three source-text assertions that a mutation survey defeated: the == 0 gate could be inverted to == 1, the claim could be wrapped in a conditional while index-ordering still passed, and deleting the post-switch markProcessed call was masked by the early-return call site. Found by a three-lens adversarial review; 16 of its 23 findings were refuted.

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