mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
Fixed #34865 -- Released memory earlier than garbage collection on database wrapping layers.
Thank you Florian Apolloner, Jake Howard and Patryk Zawadzki for the clarifying comments and reviews.
This commit is contained in:
parent
e804a07d76
commit
6a9db1e626
1
AUTHORS
1
AUTHORS
@ -349,6 +349,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Federico Capoano <nemesis@ninux.org>
|
Federico Capoano <nemesis@ninux.org>
|
||||||
Felipe Lee <felipe.lee.garcia@gmail.com>
|
Felipe Lee <felipe.lee.garcia@gmail.com>
|
||||||
Filip Noetzel <http://filip.noetzel.co.uk/>
|
Filip Noetzel <http://filip.noetzel.co.uk/>
|
||||||
|
Filip Owczarek <f.a.owczarek@gmail.com>
|
||||||
Filip Wasilewski <filip.wasilewski@gmail.com>
|
Filip Wasilewski <filip.wasilewski@gmail.com>
|
||||||
Finn Gruwier Larsen <finn@gruwier.dk>
|
Finn Gruwier Larsen <finn@gruwier.dk>
|
||||||
Fiza Ashraf <fizaashraf37@gmail.com>
|
Fiza Ashraf <fizaashraf37@gmail.com>
|
||||||
|
@ -13,6 +13,9 @@ class BaseDatabaseClient:
|
|||||||
# connection is an instance of BaseDatabaseWrapper.
|
# connection is an instance of BaseDatabaseWrapper.
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def settings_to_cmd_args_env(cls, settings_dict, parameters):
|
def settings_to_cmd_args_env(cls, settings_dict, parameters):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
|
@ -25,6 +25,9 @@ class BaseDatabaseCreation:
|
|||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
def _nodb_cursor(self):
|
def _nodb_cursor(self):
|
||||||
return self.connection._nodb_cursor()
|
return self.connection._nodb_cursor()
|
||||||
|
|
||||||
|
@ -407,6 +407,9 @@ class BaseDatabaseFeatures:
|
|||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def supports_explaining_query_execution(self):
|
def supports_explaining_query_execution(self):
|
||||||
"""Does this backend support explaining query execution?"""
|
"""Does this backend support explaining query execution?"""
|
||||||
|
@ -19,6 +19,9 @@ class BaseDatabaseIntrospection:
|
|||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
def get_field_type(self, data_type, description):
|
def get_field_type(self, data_type, description):
|
||||||
"""
|
"""
|
||||||
Hook for a database backend to use the cursor description to
|
Hook for a database backend to use the cursor description to
|
||||||
|
@ -59,6 +59,9 @@ class BaseDatabaseOperations:
|
|||||||
self.connection = connection
|
self.connection = connection
|
||||||
self._cache = None
|
self._cache = None
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
def autoinc_sql(self, table, column):
|
def autoinc_sql(self, table, column):
|
||||||
"""
|
"""
|
||||||
Return any SQL needed to support auto-incrementing primary keys, or
|
Return any SQL needed to support auto-incrementing primary keys, or
|
||||||
|
@ -4,6 +4,9 @@ class BaseDatabaseValidation:
|
|||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.connection
|
||||||
|
|
||||||
def check(self, **kwargs):
|
def check(self, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@ -64,6 +64,9 @@ class DatabaseErrorWrapper:
|
|||||||
"""
|
"""
|
||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
del self.wrapper
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import gc
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from django.db import DEFAULT_DB_ALIAS, connection, connections, transaction
|
from django.db import DEFAULT_DB_ALIAS, connection, connections, transaction
|
||||||
@ -60,6 +61,36 @@ class DatabaseWrapperTests(SimpleTestCase):
|
|||||||
with patch.object(connection.features, "minimum_database_version", None):
|
with patch.object(connection.features, "minimum_database_version", None):
|
||||||
connection.check_database_version_supported()
|
connection.check_database_version_supported()
|
||||||
|
|
||||||
|
def test_release_memory_without_garbage_collection(self):
|
||||||
|
# Schedule the restore of the garbage collection settings.
|
||||||
|
self.addCleanup(gc.set_debug, 0)
|
||||||
|
self.addCleanup(gc.enable)
|
||||||
|
|
||||||
|
# Disable automatic garbage collection to control when it's triggered,
|
||||||
|
# then run a full collection cycle to ensure `gc.garbage` is empty.
|
||||||
|
gc.disable()
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
# The garbage list isn't automatically populated to avoid CPU overhead,
|
||||||
|
# so debugging needs to be enabled to track all unreachable items and
|
||||||
|
# have them stored in `gc.garbage`.
|
||||||
|
gc.set_debug(gc.DEBUG_SAVEALL)
|
||||||
|
|
||||||
|
# Create a new connection that will be closed during the test, and also
|
||||||
|
# ensure that a `DatabaseErrorWrapper` is created for this connection.
|
||||||
|
test_connection = connection.copy()
|
||||||
|
with test_connection.wrap_database_errors:
|
||||||
|
self.assertEqual(test_connection.queries, [])
|
||||||
|
|
||||||
|
# Close the connection and remove references to it. This will mark all
|
||||||
|
# objects related to the connection as garbage to be collected.
|
||||||
|
test_connection.close()
|
||||||
|
test_connection = None
|
||||||
|
|
||||||
|
# Enforce garbage collection to populate `gc.garbage` for inspection.
|
||||||
|
gc.collect()
|
||||||
|
self.assertEqual(gc.garbage, [])
|
||||||
|
|
||||||
|
|
||||||
class DatabaseWrapperLoggingTests(TransactionTestCase):
|
class DatabaseWrapperLoggingTests(TransactionTestCase):
|
||||||
available_apps = ["backends"]
|
available_apps = ["backends"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user