Opened 3 weeks ago

Closed 3 weeks ago

#36674 closed Cleanup/optimization (fixed)

select_related() leaks memory

Reported by: Jacob Walls Owned by: nzioker
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Calls to select_related() create nested functions that aren't cleaned up until garbage collection runs:

Borrowed testing strategy from #34865:

  • tests/select_related/tests.py

    diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py
    index 41ed350cf3..252e884d7a 100644
    a b class SelectRelatedTests(TestCase):  
    242242            FETCH_PEERS,
    243243        )
    244244
     245    def test_memory_leak(self):
     246        # todo: factor out a test util
     247        import gc
     248   
     249        # Schedule the restore of the garbage collection settings.
     250        self.addCleanup(gc.set_debug, 0)
     251        self.addCleanup(gc.enable)
     252
     253        # Disable automatic garbage collection to control when it's triggered,
     254        # then run a full collection cycle to ensure `gc.garbage` is empty.
     255        gc.disable()
     256        gc.collect()
     257
     258        # The garbage list isn't automatically populated to avoid CPU overhead,
     259        # so debugging needs to be enabled to track all unreachable items and
     260        # have them stored in `gc.garbage`.
     261        gc.set_debug(gc.DEBUG_SAVEALL)
     262
     263        list(Species.objects.select_related("genus"))
     264
     265        # Enforce garbage collection to populate `gc.garbage` for inspection.
     266        gc.collect()
     267        self.assertEqual(gc.garbage, [])
     268
    245269
    246270class SelectRelatedValidationTests(SimpleTestCase):
    247271    """

Gives:

AssertionError: Lists differ: [<cell at 0x10630c940: function object at [148 chars]040>] != []

First list contains 3 additional elements.
First extra element 0:
<cell at 0x10630c940: function object at 0x1079b8040>

+ []
- [<cell at 0x10630c940: function object at 0x1079b8040>,
-  (<cell at 0x10630c940: function object at 0x1079b8040>,),
-  <function SQLCompiler.get_select.<locals>.get_select_from_parent at 0x1079b8040>]

At a glance, it looks like we can lift get_select_from_parent to a class method or all the way to module-level without a loss of functionality to address this.

Change History (9)

comment:1 by Simon Charette, 3 weeks ago

Triage Stage: UnreviewedAccepted

Good catch, looks like it could even be a staticmethod since it only cares about klass_info.

comment:2 by nzioker, 3 weeks ago

I'm working on this fix.

comment:3 by nzioker, 3 weeks ago

Owner: set to nzioker
Status: newassigned

comment:5 by Simon Charette, 3 weeks ago

Patch needs improvement: set

MR was targeting 4.2.

Also it appears that my analysis was wrong. Since we're recursively calling the function it'd be better if it was a classmethod instead so we can do cls.get_select_from_parent(ki) in the function definition. We should also do self.get_select_from_parent instead of SQLCompiler.get_select_from_parent in .get_select.

comment:7 by Jacob Walls, 3 weeks ago

Needs tests: set
Patch needs improvement: unset

comment:8 by Jacob Walls, 3 weeks ago

Needs tests: unset
Triage Stage: AcceptedReady for checkin

comment:9 by Jacob Walls <jacobtylerwalls@…>, 3 weeks ago

Resolution: fixed
Status: assignedclosed

In 3ff32c50:

Fixed #36674 -- Fixed memory leak in select_related().

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