Explorar o código

Fixed #32772 -- Made database cache count size once per set.

Michael Lissner %!s(int64=3) %!d(string=hai) anos
pai
achega
5a8e8f80bb
Modificáronse 3 ficheiros con 20 adicións e 6 borrados
  1. 1 0
      AUTHORS
  2. 6 6
      django/core/cache/backends/db.py
  3. 13 0
      tests/cache/tests.py

+ 1 - 0
AUTHORS

@@ -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>

+ 6 - 6
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])

+ 13 - 0
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.