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): 242 242 FETCH_PEERS, 243 243 ) 244 244 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 245 269 246 270 class SelectRelatedValidationTests(SimpleTestCase): 247 271 """
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 , 3 weeks ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:3 by , 3 weeks ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:5 by , 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 , 3 weeks ago
| Needs tests: | set |
|---|---|
| Patch needs improvement: | unset |
comment:8 by , 3 weeks ago
| Needs tests: | unset |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
Good catch, looks like it could even be a
staticmethodsince it only cares aboutklass_info.