Opened 10 years ago

Closed 32 hours ago

#26630 closed Bug (duplicate)

Defered constraint checks flush after `post_delete` signal

Reported by: Alex Madjar Owned by:
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords:
Cc: Annabelle Wiegart Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Repro steps:

  1. Use a database that supports can_defer_constraint_checks (tested on postgres)
  2. Have a model (A) with a null=True ForeignKey or OneToOneField (F) to another model (B)
  3. Connect a function to listen to post_delete on (B) that pulls objects of type (A) from the db (using a = A.objects.get... etc )
  4. Try to access a.F for an object a that used to be associated with the deleted instance of B that was just deleted

Expectation: a.F_id is None

Actually: a.F_id is the id of the deleted instance and a.F raises an ObjectDoesNotExist exception

Note: doesn't repro on fields where null=False or on sqlite. My diagnosis is that A's field isn't being properly cleared (or A objects deleted in the case of on delete CASCADE) because the constraint checks aren't being flushed until _after_ the post_delete signal fires

Attachments (1)

t26630.tar.gz (1013 bytes ) - added by Tim Graham 10 years ago.
sample app to reproduce

Download all attachments as: .zip

Change History (13)

comment:1 by Alex Madjar, 10 years ago

Only tried on CASCADE deletes, I think it blames to this code - https://github.com/django/django/blob/master/django/db/models/deletion.py#L15

comment:2 by Tim Graham, 10 years ago

If you could provide a test case for Django's test suite, that would be helpful.

by Tim Graham, 10 years ago

Attachment: t26630.tar.gz added

sample app to reproduce

comment:3 by Tim Graham, 10 years ago

Triage Stage: UnreviewedAccepted

Seems legitimate. I attached a sample project I used to reproduce. The first test method shows the failure.

comment:4 by Can Sarıgöl, 6 years ago

Owner: changed from nobody to Can Sarıgöl
Status: newassigned
Version: 1.9master

comment:5 by Can Sarıgöl, 6 years ago

Has patch: set

comment:6 by Mariusz Felisiak, 6 years ago

Patch needs improvement: set

comment:7 by Can Sarıgöl, 3 years ago

Owner: Can Sarıgöl removed

comment:8 by Annabelle Wiegart, 6 days ago

I'm unable to reproduce the described behavior with the attached sample project. The related Choice instance seems to be deleted immediately. The post_delete callback function produces the error t26630.models.Choice.DoesNotExist: Choice matching query does not exist. Is there still a bug that needs to be fixed?

comment:9 by Jacob Walls, 6 days ago

Thanks, Annabelle, I'm seeing the same. However, I think this is a symptom of the test project needing to be updated. If you connect a post_delete signal on Choice as well, you can undo the effect of the optimization from #30856 and trigger the failing code path again.

Here is a fiddle on DryORM

I'm not sure exactly how to fix this, but we should probably examine the solution space a bit more before closing this one.

If you're curious how I dug around for this, I noticed the original report suggesting that one of the objects was not being cascade deleted, but as you saw, it was. So I set a breakpoint and noticed we were hitting a "fast delete" path, which led me to #30856 driving more traffic into that path.

comment:10 by Annabelle Wiegart, 42 hours ago

Thank you for your clarifications, Jacob. i looked at the DryORM example and a bit into the Collector class and the "fast delete" path.

I'm a bit unsure about the expected behavior though. The ticket description says "Expectation: a.F_id is None". Since the "fast delete" path leads to the deletion of a not being deferred, I suppose the expected behavior would be exactly this, even if a post_delete signal is attached to Choice as well?

comment:11 by Annabelle Wiegart, 35 hours ago

Cc: Annabelle Wiegart added

comment:12 by Jacob Walls, 32 hours ago

Resolution: duplicate
Status: assignedclosed

I'm also a little unsure. I think the gist of the report is that doing database queries during a post_delete listener is problematic, because you can query Choice rows by question_id and get misleading results (as those rows are in the middle of being cascade deleted).

I'm reading the original report to suggest reordering the post_delete signal to run after constraints are flushed (and all cascade deletes have happened). I doubt that would be backward compatible.

In #30835 something similar was suggested, and Simon explained you can use on_commit() to defer execution of the callback (to a point when all cascade deletions have been made). I think I'd suggest rewriting the signal handler that way to avoid this problem with unstable Choice results.

I'm inclined to close this as duplicate of #30835. As mentioned there, if there's a proposal for a docs tweak in post_delete we could consider it. (In that case, do reopen #30835!)

Thanks for helping out with triage.

Note: See TracTickets for help on using tickets.
Back to Top