diff options
author | Michael Lissner <mlissner@michaeljaylissner.com> | 2021-05-25 09:31:26 -0700 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-05-26 11:21:11 +0200 |
commit | 5a8e8f80bb82a867eab7e4d9d099f21d0a976d22 (patch) | |
tree | d59a000cab2fda9925951d2c5ed3b942a94a3bd4 | |
parent | 12b19a1d76e1a6f80923c8358290d605dacd65d4 (diff) | |
download | django-5a8e8f80bb82a867eab7e4d9d099f21d0a976d22.tar.gz |
Fixed #32772 -- Made database cache count size once per set.
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | django/core/cache/backends/db.py | 12 | ||||
-rw-r--r-- | tests/cache/tests.py | 13 |
3 files changed, 20 insertions, 6 deletions
@@ -641,6 +641,7 @@ answer newbie questions, and generally made Django that much better: Michael S. Brown <michael@msbrown.net> Michael Hall <mhall1@ualberta.ca> Michael Josephson <http://www.sdjournal.com/> + Michael Lissner <mike@free.law> Michael Manfre <mmanfre@gmail.com> michael.mcewan@gmail.com Michael Placentra II <someone@michaelplacentra2.net> diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py index d62083a0f6..b84885f1ef 100644 --- a/django/core/cache/backends/db.py +++ b/django/core/cache/backends/db.py @@ -128,7 +128,7 @@ class DatabaseCache(BaseDatabaseCache): exp = datetime.fromtimestamp(timeout, tz=tz) exp = exp.replace(microsecond=0) if num > self._max_entries: - self._cull(db, cursor, now) + self._cull(db, cursor, now, num) pickled = pickle.dumps(value, self.pickle_protocol) # The DB column is expecting a string, so make sure the value is a # string, not bytes. Refs #19274. @@ -247,7 +247,7 @@ class DatabaseCache(BaseDatabaseCache): ) return cursor.fetchone() is not None - def _cull(self, db, cursor, now): + def _cull(self, db, cursor, now, num): if self._cull_frequency == 0: self.clear() else: @@ -255,10 +255,10 @@ class DatabaseCache(BaseDatabaseCache): table = connection.ops.quote_name(self._table) cursor.execute("DELETE FROM %s WHERE expires < %%s" % table, [connection.ops.adapt_datetimefield_value(now)]) - cursor.execute("SELECT COUNT(*) FROM %s" % table) - num = cursor.fetchone()[0] - if num > self._max_entries: - cull_num = num // self._cull_frequency + deleted_count = cursor.rowcount + remaining_num = num - deleted_count + if remaining_num > self._max_entries: + cull_num = remaining_num // self._cull_frequency cursor.execute( connection.ops.cache_key_culling_sql() % table, [cull_num]) diff --git a/tests/cache/tests.py b/tests/cache/tests.py index 65a8f12fc7..783055182d 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -40,6 +40,7 @@ from django.test import ( ignore_warnings, override_settings, ) from django.test.signals import setting_changed +from django.test.utils import CaptureQueriesContext from django.utils import timezone, translation from django.utils.cache import ( get_cache_key, learn_cache_key, patch_cache_control, patch_vary_headers, @@ -1117,6 +1118,18 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase): with self.assertNumQueries(1): cache.delete_many(['a', 'b', 'c']) + def test_cull_count_queries(self): + old_max_entries = cache._max_entries + # Force _cull to delete on first cached record. + cache._max_entries = -1 + with CaptureQueriesContext(connection) as captured_queries: + try: + cache.set('force_cull', 'value', 1000) + finally: + cache._max_entries = old_max_entries + num_count_queries = sum('COUNT' in query['sql'] for query in captured_queries) + self.assertEqual(num_count_queries, 1) + def test_delete_cursor_rowcount(self): """ The rowcount attribute should not be checked on a closed cursor. |