Skip to content

fix: release SAVEPOINT via context manager in remaining begin_nested() call sites - #14032

Open
stilla[bot] wants to merge 1 commit into
mainfrom
stilla/fix-nested-savepoint-leaks
Open

fix: release SAVEPOINT via context manager in remaining begin_nested() call sites#14032
stilla[bot] wants to merge 1 commit into
mainfrom
stilla/fix-nested-savepoint-leaks

Conversation

@stilla

@stilla stilla Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Problem

#14030 fixed CustomerMeterRepository.get_or_create where self.session.begin_nested() was opened manually and only rolled back on the IntegrityError path, leaving the SAVEPOINT un-released on success. Looped over many meters this stacked hundreds of un-released SAVEPOINTs, and a later session rollback recursed once per stacked SAVEPOINT, causing a production RecursionError (Sentry SERVER-4ZT).

That PR's description flagged the same pattern elsewhere as worth auditing separately. This PR is that follow-up.

Fix

Converted every remaining manual begin_nested() leak site to async with session.begin_nested():, which releases the SAVEPOINT automatically on success and rolls it back automatically on exception:

  • discount/service.py: create(), and the update() products block
  • event_type/repository.py: get_or_create() (same shape as the fix(customer_meter): release SAVEPOINT on success in get_or_create #14030 fix)
  • integrations/github_repository_benefit/service.py: create_oauth_account() (was manually calling nested.commit()/nested.rollback())
  • organization/service.py: create(), and add_user()
  • product/service.py: update()'s medias and attached_custom_fields blocks. These accumulate validation errors instead of raising immediately, so a small local sentinel exception is used to trigger the context manager's rollback while preserving the exact existing control flow (both blocks always run, a combined PolarRequestValidationError is still raised at the end with identical content)
  • user/service.py: get_by_email_or_create()

Left untouched

  • customer_meter/repository.py::get_or_create — already fixed in fix(customer_meter): release SAVEPOINT on success in get_or_create #14030
  • member/service.py, customer_seat/service.py, transaction/service/dispute.py — already use the context manager pattern
  • subscription/service.py's charge-preview and change-preview methods — these intentionally always roll back the SAVEPOINT (try/finally: rollback) to compute-then-discard a preview. Converting them would incorrectly persist the preview changes instead of discarding them
  • Test scaffolding under server/tests/

Testing

ruff check/format and mypy clean on touched files. Full test suites for discount, event_type, organization, product, user, and integrations/github_repository_benefit: 1124 passed, 16 skipped, no failures.

Follow-up to #14030, requested by @frankie567 from @Stilla investigation.

Sent by @frankie567 from PR 14030 savepoint context manager fix.

Review in cubic

Follow-up to #14030. Several call sites opened a SAVEPOINT with a manual
`session.begin_nested()` and only rolled it back on the error path, leaving
the SAVEPOINT un-released on success. Over many iterations these stack up on a
session and cause a RecursionError when the session is eventually rolled back.

Convert each remaining occurrence to the `async with session.begin_nested():`
context-manager form, which releases the SAVEPOINT on success and rolls it back
automatically on exception, while preserving each call site's exact control
flow, exceptions, return values, and logging.

For the product update paths that accumulate validation errors across blocks
(medias, attached custom fields), a small local `_SavepointRollback` sentinel
triggers the context manager's automatic rollback-and-continue without changing
observable behavior.

Intentional dry-run/preview SAVEPOINTs (subscription charge/change previews)
and already-fixed/already-converted sites are left untouched.

Co-Authored-By: Stilla <stilla@stilla.ai>
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
orbit Ready Ready Preview Aug 28, 2026 3:42pm
polar-test Ready Ready Preview Aug 28, 2026 3:42pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

OpenAPI Changes

No changes detected in the OpenAPI schema.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant