#21169: Deletion in custom reverse managers
-------------------------------------+-------------------------------------
     Reporter:  sebastian            |                    Owner:  loic84
         Type:  Bug                  |                   Status:  assigned
    Component:  Database layer       |                  Version:  master
  (models, ORM)                      |               Resolution:
     Severity:  Release blocker      |             Triage Stage:  Accepted
     Keywords:                       |      Needs documentation:  0
    Has patch:  0                    |  Patch needs improvement:  0
  Needs tests:  0                    |                    UI/UX:  0
Easy pickings:  0                    |
-------------------------------------+-------------------------------------

Comment (by akaariai):

 I did some work to make sure queries used by m2m clear() and remove() are
 as simple as possible. Now the clearing queries do not use subqueries when
 the manager doesn't have filters. The subqueries in these cases didn't
 actually filter anything out. In addition, when subqueries are used, the
 subquery has one less join than before. End result is that the queries are
 similar to current master when the base manager doesn't have filters, and
 a bit more efficient that current HEAD of pull 1685 when there are
 filters. The tradeoff is even more complexity in m2m manager
 implementation.

 To me it seems important that as simple as possible queries are used. This
 ensures as little DB resources are used as possible. In addition I believe
 old versions of MySQL will simply panic when they see a query with both a
 subquery and joins...

 Patch at
 
https://github.com/akaariai/django/commit/9dd75213050e6f85a0b93ddd59243f8d3809b06c
 - this doesn't have any tests, and regressions in query efficiency seem
 likely. I wonder if CaptureQueriesContext could be used to inspect the
 used queries.

 BTW I spotted one possible data-corruption issue in current clear() code
 for symmetric m2m. Currently the clear is implemented as two separate
 delete queries which delete all items from the relation, first in one
 direction, then in another. But, if a concurrent add() commits in between
 then the added relation is deleted in one direction, but not in anohter.
 The sequence is like this for transactions T1 and T2:
 {{{
 T1: add relation for instances m1 <-> m2. Two rows are added so that the
     relation is symmetrical (rows m1:m2 and m2:m1).
 T2: m1.clear() starts. First, delete all rows where m1:*. m1:m2 isn't
     deleted as that isn't yet visible.
 T1: commits, m1:m2 and m2:m1 become visible to T2.
 T2: Second part of clear is executed, that is *:m1 is deleted. Now m2:m1
     is visible and it is deleted.
 T2: commits. m1:m2 relation exists, but m2:m1 doesn't. The symmetry of
     m1<->m2 is broken.
 }}}
 Luckily this seems rare in practice. pull 1685 does the delete in one go,
 so this issue is fixed in PR1685. This seems to be impossible to test in a
 way that the regression test actually picks up regressions. So, no tests
 needed for this case.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/21169#comment:8>
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 django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.da8842d19cf151b3ed46c9e3e926a71c%40djangoproject.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to