Fixed #29626, #29584 -- Added optimized versions of get_many() and delete_many() for the db cache backend.
This commit is contained in:
parent
c02d473781
commit
abd0ad7681
|
@ -49,8 +49,17 @@ class DatabaseCache(BaseDatabaseCache):
|
||||||
pickle_protocol = pickle.HIGHEST_PROTOCOL
|
pickle_protocol = pickle.HIGHEST_PROTOCOL
|
||||||
|
|
||||||
def get(self, key, default=None, version=None):
|
def get(self, key, default=None, version=None):
|
||||||
key = self.make_key(key, version=version)
|
return self.get_many([key], version).get(key, default)
|
||||||
self.validate_key(key)
|
|
||||||
|
def get_many(self, keys, version=None):
|
||||||
|
if not keys:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
key_map = {}
|
||||||
|
for key in keys:
|
||||||
|
self.validate_key(key)
|
||||||
|
key_map[self.make_key(key, version)] = key
|
||||||
|
|
||||||
db = router.db_for_read(self.cache_model_class)
|
db = router.db_for_read(self.cache_model_class)
|
||||||
connection = connections[db]
|
connection = connections[db]
|
||||||
quote_name = connection.ops.quote_name
|
quote_name = connection.ops.quote_name
|
||||||
|
@ -58,43 +67,36 @@ class DatabaseCache(BaseDatabaseCache):
|
||||||
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
'SELECT %s, %s, %s FROM %s WHERE %s = %%s' % (
|
'SELECT %s, %s, %s FROM %s WHERE %s IN (%s)' % (
|
||||||
quote_name('cache_key'),
|
quote_name('cache_key'),
|
||||||
quote_name('value'),
|
quote_name('value'),
|
||||||
quote_name('expires'),
|
quote_name('expires'),
|
||||||
table,
|
table,
|
||||||
quote_name('cache_key'),
|
quote_name('cache_key'),
|
||||||
|
', '.join(['%s'] * len(key_map)),
|
||||||
),
|
),
|
||||||
[key]
|
list(key_map),
|
||||||
)
|
)
|
||||||
row = cursor.fetchone()
|
rows = cursor.fetchall()
|
||||||
if row is None:
|
|
||||||
return default
|
|
||||||
|
|
||||||
expires = row[2]
|
result = {}
|
||||||
|
expired_keys = []
|
||||||
expression = models.Expression(output_field=models.DateTimeField())
|
expression = models.Expression(output_field=models.DateTimeField())
|
||||||
for converter in (connection.ops.get_db_converters(expression) +
|
converters = (connection.ops.get_db_converters(expression) + expression.get_db_converters(connection))
|
||||||
expression.get_db_converters(connection)):
|
for key, value, expires in rows:
|
||||||
if func_supports_parameter(converter, 'context'): # RemovedInDjango30Warning
|
for converter in converters:
|
||||||
expires = converter(expires, expression, connection, {})
|
if func_supports_parameter(converter, 'context'): # RemovedInDjango30Warning
|
||||||
|
expires = converter(expires, expression, connection, {})
|
||||||
|
else:
|
||||||
|
expires = converter(expires, expression, connection)
|
||||||
|
if expires < timezone.now():
|
||||||
|
expired_keys.append(key)
|
||||||
else:
|
else:
|
||||||
expires = converter(expires, expression, connection)
|
value = connection.ops.process_clob(value)
|
||||||
|
value = pickle.loads(base64.b64decode(value.encode()))
|
||||||
if expires < timezone.now():
|
result[key_map.get(key)] = value
|
||||||
db = router.db_for_write(self.cache_model_class)
|
self._base_delete_many(expired_keys)
|
||||||
connection = connections[db]
|
return result
|
||||||
with connection.cursor() as cursor:
|
|
||||||
cursor.execute(
|
|
||||||
'DELETE FROM %s WHERE %s = %%s' % (
|
|
||||||
table,
|
|
||||||
quote_name('cache_key'),
|
|
||||||
),
|
|
||||||
[key]
|
|
||||||
)
|
|
||||||
return default
|
|
||||||
|
|
||||||
value = connection.ops.process_clob(row[1])
|
|
||||||
return pickle.loads(base64.b64decode(value.encode()))
|
|
||||||
|
|
||||||
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
||||||
key = self.make_key(key, version=version)
|
key = self.make_key(key, version=version)
|
||||||
|
@ -202,15 +204,33 @@ class DatabaseCache(BaseDatabaseCache):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def delete(self, key, version=None):
|
def delete(self, key, version=None):
|
||||||
key = self.make_key(key, version=version)
|
self.delete_many([key], version)
|
||||||
self.validate_key(key)
|
|
||||||
|
def delete_many(self, keys, version=None):
|
||||||
|
key_list = []
|
||||||
|
for key in keys:
|
||||||
|
self.validate_key(key)
|
||||||
|
key_list.append(self.make_key(key, version))
|
||||||
|
self._base_delete_many(key_list)
|
||||||
|
|
||||||
|
def _base_delete_many(self, keys):
|
||||||
|
if not keys:
|
||||||
|
return
|
||||||
|
|
||||||
db = router.db_for_write(self.cache_model_class)
|
db = router.db_for_write(self.cache_model_class)
|
||||||
connection = connections[db]
|
connection = connections[db]
|
||||||
table = connection.ops.quote_name(self._table)
|
quote_name = connection.ops.quote_name
|
||||||
|
table = quote_name(self._table)
|
||||||
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
|
cursor.execute(
|
||||||
|
'DELETE FROM %s WHERE %s IN (%s)' % (
|
||||||
|
table,
|
||||||
|
quote_name('cache_key'),
|
||||||
|
', '.join(['%s'] * len(keys)),
|
||||||
|
),
|
||||||
|
keys,
|
||||||
|
)
|
||||||
|
|
||||||
def has_key(self, key, version=None):
|
def has_key(self, key, version=None):
|
||||||
key = self.make_key(key, version=version)
|
key = self.make_key(key, version=version)
|
||||||
|
|
|
@ -1005,6 +1005,20 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
table_name = connection.ops.quote_name('test cache table')
|
table_name = connection.ops.quote_name('test cache table')
|
||||||
cursor.execute('DROP TABLE %s' % table_name)
|
cursor.execute('DROP TABLE %s' % table_name)
|
||||||
|
|
||||||
|
def test_get_many_num_queries(self):
|
||||||
|
cache.set_many({'a': 1, 'b': 2})
|
||||||
|
cache.set('expired', 'expired', 0.01)
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.assertEqual(cache.get_many(['a', 'b']), {'a': 1, 'b': 2})
|
||||||
|
time.sleep(0.02)
|
||||||
|
with self.assertNumQueries(2):
|
||||||
|
self.assertEqual(cache.get_many(['a', 'b', 'expired']), {'a': 1, 'b': 2})
|
||||||
|
|
||||||
|
def test_delete_many_num_queries(self):
|
||||||
|
cache.set_many({'a': 1, 'b': 2, 'c': 3})
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
cache.delete_many(['a', 'b', 'c'])
|
||||||
|
|
||||||
def test_zero_cull(self):
|
def test_zero_cull(self):
|
||||||
self._perform_cull_test(caches['zero_cull'], 50, 18)
|
self._perform_cull_test(caches['zero_cull'], 50, 18)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue