Fixed #11569 -- Wrapped DatabaseCache._base_set in an atomic block.

The atomic block provides a clean rollback to a savepoint on failed writes.

The ticket reported a race condition which I don't know how to test.
This commit is contained in:
Aymeric Augustin 2013-03-11 22:16:37 +01:00
parent faabf3614e
commit 1b12e248ea
1 changed files with 15 additions and 14 deletions

View File

@ -10,7 +10,7 @@ except ImportError:
from django.conf import settings from django.conf import settings
from django.core.cache.backends.base import BaseCache from django.core.cache.backends.base import BaseCache
from django.db import connections, router, DatabaseError from django.db import connections, transaction, router, DatabaseError
from django.utils import timezone, six from django.utils import timezone, six
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
@ -26,7 +26,7 @@ class Options(object):
self.model_name = 'cacheentry' self.model_name = 'cacheentry'
self.verbose_name = 'cache entry' self.verbose_name = 'cache entry'
self.verbose_name_plural = 'cache entries' self.verbose_name_plural = 'cache entries'
self.object_name = 'CacheEntry' self.object_name = 'CacheEntry'
self.abstract = False self.abstract = False
self.managed = True self.managed = True
self.proxy = False self.proxy = False
@ -108,19 +108,20 @@ class DatabaseCache(BaseDatabaseCache):
# string, not bytes. Refs #19274. # string, not bytes. Refs #19274.
if six.PY3: if six.PY3:
b64encoded = b64encoded.decode('latin1') b64encoded = b64encoded.decode('latin1')
cursor.execute("SELECT cache_key, expires FROM %s "
"WHERE cache_key = %%s" % table, [key])
try: try:
result = cursor.fetchone() with transaction.atomic_if_autocommit(using=db):
if result and (mode == 'set' or cursor.execute("SELECT cache_key, expires FROM %s "
(mode == 'add' and result[1] < now)): "WHERE cache_key = %%s" % table, [key])
cursor.execute("UPDATE %s SET value = %%s, expires = %%s " result = cursor.fetchone()
"WHERE cache_key = %%s" % table, exp = connections[db].ops.value_to_db_datetime(exp)
[b64encoded, connections[db].ops.value_to_db_datetime(exp), key]) if result and (mode == 'set' or (mode == 'add' and result[1] < now)):
else: cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
cursor.execute("INSERT INTO %s (cache_key, value, expires) " "WHERE cache_key = %%s" % table,
"VALUES (%%s, %%s, %%s)" % table, [b64encoded, exp, key])
[key, b64encoded, connections[db].ops.value_to_db_datetime(exp)]) else:
cursor.execute("INSERT INTO %s (cache_key, value, expires) "
"VALUES (%%s, %%s, %%s)" % table,
[key, b64encoded, exp])
except DatabaseError: except DatabaseError:
# To be threadsafe, updates/inserts are allowed to fail silently # To be threadsafe, updates/inserts are allowed to fail silently
return False return False