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
@ -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')
try:
with transaction.atomic_if_autocommit(using=db):
cursor.execute("SELECT cache_key, expires FROM %s " cursor.execute("SELECT cache_key, expires FROM %s "
"WHERE cache_key = %%s" % table, [key]) "WHERE cache_key = %%s" % table, [key])
try:
result = cursor.fetchone() result = cursor.fetchone()
if result and (mode == 'set' or exp = connections[db].ops.value_to_db_datetime(exp)
(mode == 'add' and result[1] < now)): if result and (mode == 'set' or (mode == 'add' and result[1] < now)):
cursor.execute("UPDATE %s SET value = %%s, expires = %%s " cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
"WHERE cache_key = %%s" % table, "WHERE cache_key = %%s" % table,
[b64encoded, connections[db].ops.value_to_db_datetime(exp), key]) [b64encoded, exp, key])
else: else:
cursor.execute("INSERT INTO %s (cache_key, value, expires) " cursor.execute("INSERT INTO %s (cache_key, value, expires) "
"VALUES (%%s, %%s, %%s)" % table, "VALUES (%%s, %%s, %%s)" % table,
[key, b64encoded, connections[db].ops.value_to_db_datetime(exp)]) [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