#35279: Memory Leak with `prefetch_related`
-----------------------------------------+-----------------------------
               Reporter:  canton         |          Owner:  nobody
                   Type:  Bug            |         Status:  new
              Component:  Uncategorized  |        Version:  4.2
               Severity:  Normal         |       Keywords:  memory leak
           Triage Stage:  Unreviewed     |      Has patch:  0
    Needs documentation:  0              |    Needs tests:  0
Patch needs improvement:  0              |  Easy pickings:  0
                  UI/UX:  0              |
-----------------------------------------+-----------------------------
 Memory Leak after calling `queryset.prefetch_related()` or
 `prefetch_related_objects()`

 To reproduce:

 {{{
 import gc
 from django.db import models
 from django.db.models import prefetch_related_objects


 class Foo(models.Model):
     id = models.AutoField(primary_key=True)


 class Bar(models.Model):
     id = models.AutoField(primary_key=True)
     foo = models.ForeignKey(Foo, on_delete=models.CASCADE)


 def prepare_data():
     if Foo.objects.exists():
         return
     foo = Foo()
     foo.save()
     bar = Bar(foo=foo)
     bar.save()


 def test1():
     # no prefetch
     for foo in Foo.objects.all():
         for bar in foo.bar_set.all():
             print(foo.id, bar.id)


 def test2():
     # queryset.prefetch_related()
     for foo in Foo.objects.prefetch_related("bar_set").all():
         for bar in foo.bar_set.all():
             print(foo.id, bar.id)


 def test3():
     # prefetch_related_objects()
     foo_list = list(Foo.objects.all())
     prefetch_related_objects(foo_list, "bar_set")
     for foo in foo_list:
         for bar in foo.bar_set.all():
             print(foo.id, bar.id)


 def run():
     prepare_data()

     # warn up
     test1()
     test2()
     test3()

     gc.collect()

     gc.set_debug(gc.DEBUG_LEAK)

     gc.collect()
     print(f"baseline - garbage count: {len(gc.garbage)}")

     test1()
     gc.collect()
     print(f"test1 - garbage count: {len(gc.garbage)}")

     test2()
     gc.collect()
     print(f"test2 - garbage count: {len(gc.garbage)}")

     test3()
     gc.collect()
     print(f"test3 - garbage count: {len(gc.garbage)}")

     gc.set_debug(0)


 run()
 }}}


 Output
 {{{
 1 1
 1 1
 1 1
 baseline - garbage count: 0
 1 1
 test1 - garbage count: 0  # no memory leak
 1 1
 test2 - garbage count: 23  # 23 objects leaked
 1 1
 test3 - garbage count: 46  # another 23 objects leaked
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35279>
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 on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018e18508607-55fd6223-5289-406d-a678-293bc8f21e1c-000000%40eu-central-1.amazonses.com.

Reply via email to