#37000: cursor_iter relies on GC for server-side cursor cleanup, causing
transaction abort after savepoint rollback
-------------------------------------+-------------------------------------
Reporter: Ratskó László | Owner: (none)
Type: Bug | Status: new
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution:
Keywords: iterator, server- | Triage Stage: Accepted
side-cursor, savepoint, psycopg3 |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Ratskó László):
Thanks for accepting the ticket!
To clarify: yes, this is specifically a nested transaction.atomic() issue
(savepoints). We have ATOMIC_REQUESTS = True, so every request is already
in a transaction, and any transaction.atomic() in our code creates a
savepoint. Sorry for not mentioning that in the report.
Our pattern follows the documented approach — catching exceptions outside
the atomic block:
{{{
# ATOMIC_REQUESTS = True → request is already in a transaction
def export_view(request):
try:
with transaction.atomic(): # this creates a savepoint (nested)
for item in queryset.iterator(): # server-side cursor
process(item) # may raise
except SomeError:
save_error_status() # ← fails with InFailedSqlTransaction
}}}
This matches the pattern from the docs:
{{{
@transaction.atomic
def viewfunc(request):
create_parent()
try:
with transaction.atomic():
generate_relationships()
except IntegrityError:
handle_exception()
add_children()
}}}
We tested both scenarios:
Nested (savepoint) — FAILS:
{{{
with transaction.atomic(): # outer (ATOMIC_REQUESTS)
try:
with transaction.atomic(): # inner → savepoint
for item in queryset.iterator():
raise ValueError()
except ValueError:
Model.objects.create(...) # ← InFailedSqlTransaction
}}}
Non-nested (full rollback) — WORKS:
{{{
try:
with transaction.atomic(): # not nested → full transaction
for item in queryset.iterator():
raise ValueError()
except ValueError:
Model.objects.create(...) # ← OK, transaction is IDLE after
full rollback
}}}
Let me know if we can help somehow by providing more information. Thanks
again!
--
Ticket URL: <https://code.djangoproject.com/ticket/37000#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019d1c306938-e860aa63-0332-4087-bc06-45df7cdee8d3-000000%40eu-central-1.amazonses.com.