blob: 886d778c28005f0e17fb0d83eaaa1196ea30085e [file] [log] [blame]
adrian 2287 3f0790d500a9 2006-02-24: 1: "Database cache backend."
jezdez 16338 bf038debb499 2011-07-13: 2: import base64
jezdez 16338 bf038debb499 2011-07-13: 3: import time
jezdez 16338 bf038debb499 2011-07-13: 4: from datetime import datetime
adrian 2287 3f0790d500a9 2006-02-24: 2:
adrian 2287 3f0790d500a9 2006-02-24: 7: try:
adrian 2287 3f0790d500a9 2006-02-24: 8: import cPickle as pickle
adrian 2287 3f0790d500a9 2006-02-24: 9: except ImportError:
adrian 2287 3f0790d500a9 2006-02-24: 10: import pickle
adrian 2287 3f0790d500a9 2006-02-24: 11:
jezdez 16338 bf038debb499 2011-07-13: 11: from django.core.cache.backends.base import BaseCache
jezdez 16338 bf038debb499 2011-07-13: 12: from django.db import connections, router, transaction, DatabaseError
jezdez 16338 bf038debb499 2011-07-13: 13:
jezdez 16338 bf038debb499 2011-07-13: 14:
russellm 13275 4dd52d4f3f50 2010-08-05: 12: class Options(object):
russellm 13275 4dd52d4f3f50 2010-08-05: 13: """A class that will quack like a Django model _meta class.
russellm 13275 4dd52d4f3f50 2010-08-05: 14:
russellm 13275 4dd52d4f3f50 2010-08-05: 15: This allows cache operations to be controlled by the router
russellm 13275 4dd52d4f3f50 2010-08-05: 16: """
russellm 13275 4dd52d4f3f50 2010-08-05: 17: def __init__(self, table):
russellm 13275 4dd52d4f3f50 2010-08-05: 18: self.db_table = table
russellm 13275 4dd52d4f3f50 2010-08-05: 19: self.app_label = 'django_cache'
russellm 13275 4dd52d4f3f50 2010-08-05: 20: self.module_name = 'cacheentry'
russellm 13275 4dd52d4f3f50 2010-08-05: 21: self.verbose_name = 'cache entry'
russellm 13275 4dd52d4f3f50 2010-08-05: 22: self.verbose_name_plural = 'cache entries'
russellm 13275 4dd52d4f3f50 2010-08-05: 23: self.object_name = 'CacheEntry'
russellm 13275 4dd52d4f3f50 2010-08-05: 24: self.abstract = False
russellm 13275 4dd52d4f3f50 2010-08-05: 25: self.managed = True
russellm 13275 4dd52d4f3f50 2010-08-05: 26: self.proxy = False
russellm 13275 4dd52d4f3f50 2010-08-05: 27:
russellm 14807 905c4a7e1f19 2010-12-21: 28: class BaseDatabaseCache(BaseCache):
russellm 14807 905c4a7e1f19 2010-12-21: 29: def __init__(self, table, params):
russellm 14807 905c4a7e1f19 2010-12-21: 30: BaseCache.__init__(self, params)
russellm 13275 4dd52d4f3f50 2010-08-05: 31: self._table = table
russellm 13275 4dd52d4f3f50 2010-08-05: 32:
russellm 13275 4dd52d4f3f50 2010-08-05: 33: class CacheEntry(object):
russellm 13275 4dd52d4f3f50 2010-08-05: 34: _meta = Options(table)
russellm 13275 4dd52d4f3f50 2010-08-05: 35: self.cache_model_class = CacheEntry
russellm 13275 4dd52d4f3f50 2010-08-05: 36:
russellm 14807 905c4a7e1f19 2010-12-21: 37: class DatabaseCache(BaseDatabaseCache):
russellm 14425 793e94ba2037 2010-11-19: 38: def get(self, key, default=None, version=None):
russellm 14425 793e94ba2037 2010-11-19: 39: key = self.make_key(key, version=version)
mtredinnick 13568 7e0b7bf098d2 2010-09-12: 49: self.validate_key(key)
russellm 13275 4dd52d4f3f50 2010-08-05: 49: db = router.db_for_read(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05: 50: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05: 51: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05: 52:
russellm 13275 4dd52d4f3f50 2010-08-05: 53: cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % table, [key])
adrian 2287 3f0790d500a9 2006-02-24: 30: row = cursor.fetchone()
adrian 2287 3f0790d500a9 2006-02-24: 31: if row is None:
adrian 2287 3f0790d500a9 2006-02-24: 32: return default
adrian 2287 3f0790d500a9 2006-02-24: 33: now = datetime.now()
adrian 2287 3f0790d500a9 2006-02-24: 34: if row[2] < now:
russellm 13275 4dd52d4f3f50 2010-08-05: 59: db = router.db_for_write(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05: 60: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05: 61: cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
russellm 13275 4dd52d4f3f50 2010-08-05: 62: transaction.commit_unless_managed(using=db)
adrian 2287 3f0790d500a9 2006-02-24: 37: return default
russellm 13275 4dd52d4f3f50 2010-08-05: 64: value = connections[db].ops.process_clob(row[1])
ikelly 9862 8739c35149dd 2009-03-13: 39: return pickle.loads(base64.decodestring(value))
adrian 2287 3f0790d500a9 2006-02-24: 39:
russellm 14425 793e94ba2037 2010-11-19: 59: def set(self, key, value, timeout=None, version=None):
russellm 14425 793e94ba2037 2010-11-19: 60: key = self.make_key(key, version=version)
mtredinnick 13568 7e0b7bf098d2 2010-09-12: 69: self.validate_key(key)
mtredinnick 8110 c787c7f62142 2008-08-10: 41: self._base_set('set', key, value, timeout)
mtredinnick 6421 b4175804cc7b 2007-10-20: 45:
russellm 14425 793e94ba2037 2010-11-19: 64: def add(self, key, value, timeout=None, version=None):
russellm 14425 793e94ba2037 2010-11-19: 65: key = self.make_key(key, version=version)
mtredinnick 13568 7e0b7bf098d2 2010-09-12: 73: self.validate_key(key)
mtredinnick 6438 5096a56a9ac6 2007-10-21: 44: return self._base_set('add', key, value, timeout)
mtredinnick 6438 5096a56a9ac6 2007-10-21: 45:
mtredinnick 6421 b4175804cc7b 2007-10-20: 46: def _base_set(self, mode, key, value, timeout=None):
adrian 2287 3f0790d500a9 2006-02-24: 41: if timeout is None:
adrian 2287 3f0790d500a9 2006-02-24: 42: timeout = self.default_timeout
russellm 13275 4dd52d4f3f50 2010-08-05: 76: db = router.db_for_write(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05: 77: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05: 78: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05: 79:
russellm 13275 4dd52d4f3f50 2010-08-05: 80: cursor.execute("SELECT COUNT(*) FROM %s" % table)
adrian 2287 3f0790d500a9 2006-02-24: 45: num = cursor.fetchone()[0]
adrian 2287 3f0790d500a9 2006-02-24: 46: now = datetime.now().replace(microsecond=0)
adrian 2287 3f0790d500a9 2006-02-24: 47: exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0)
adrian 2287 3f0790d500a9 2006-02-24: 48: if num > self._max_entries:
russellm 13275 4dd52d4f3f50 2010-08-05: 85: self._cull(db, cursor, now)
adrian 2287 3f0790d500a9 2006-02-24: 50: encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
russellm 13275 4dd52d4f3f50 2010-08-05: 87: cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % table, [key])
adrian 2287 3f0790d500a9 2006-02-24: 52: try:
mtredinnick 9753 0ab5ef23e1bc 2009-03-02: 59: result = cursor.fetchone()
mtredinnick 9753 0ab5ef23e1bc 2009-03-02: 60: if result and (mode == 'set' or
mtredinnick 9753 0ab5ef23e1bc 2009-03-02: 61: (mode == 'add' and result[1] < now)):
russellm 13275 4dd52d4f3f50 2010-08-05: 92: cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % table,
russellm 13275 4dd52d4f3f50 2010-08-05: 93: [encoded, connections[db].ops.value_to_db_datetime(exp), key])
adrian 2287 3f0790d500a9 2006-02-24: 55: else:
russellm 13275 4dd52d4f3f50 2010-08-05: 95: cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % table,
russellm 13275 4dd52d4f3f50 2010-08-05: 96: [key, encoded, connections[db].ops.value_to_db_datetime(exp)])
adrian 2287 3f0790d500a9 2006-02-24: 57: except DatabaseError:
adrian 2287 3f0790d500a9 2006-02-24: 58: # To be threadsafe, updates/inserts are allowed to fail silently
russellm 13275 4dd52d4f3f50 2010-08-05: 99: transaction.rollback_unless_managed(using=db)
mtredinnick 8110 c787c7f62142 2008-08-10: 65: return False
adrian 2287 3f0790d500a9 2006-02-24: 60: else:
russellm 13275 4dd52d4f3f50 2010-08-05:102: transaction.commit_unless_managed(using=db)
mtredinnick 8110 c787c7f62142 2008-08-10: 68: return True
adrian 2287 3f0790d500a9 2006-02-24: 62:
russellm 14425 793e94ba2037 2010-11-19:101: def delete(self, key, version=None):
russellm 14425 793e94ba2037 2010-11-19:102: key = self.make_key(key, version=version)
mtredinnick 13568 7e0b7bf098d2 2010-09-12:109: self.validate_key(key)
russellm 14425 793e94ba2037 2010-11-19:104:
russellm 13275 4dd52d4f3f50 2010-08-05:106: db = router.db_for_write(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05:107: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05:108: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05:109:
russellm 13275 4dd52d4f3f50 2010-08-05:110: cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
russellm 13275 4dd52d4f3f50 2010-08-05:111: transaction.commit_unless_managed(using=db)
adrian 2287 3f0790d500a9 2006-02-24: 67:
russellm 14425 793e94ba2037 2010-11-19:112: def has_key(self, key, version=None):
russellm 14425 793e94ba2037 2010-11-19:113: key = self.make_key(key, version=version)
mtredinnick 13568 7e0b7bf098d2 2010-09-12:118: self.validate_key(key)
russellm 14425 793e94ba2037 2010-11-19:115:
russellm 13275 4dd52d4f3f50 2010-08-05:114: db = router.db_for_read(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05:115: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05:116: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05:117:
mtredinnick 9753 0ab5ef23e1bc 2009-03-02: 78: now = datetime.now().replace(microsecond=0)
russellm 13275 4dd52d4f3f50 2010-08-05:119: cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % table,
russellm 13275 4dd52d4f3f50 2010-08-05:120: [key, connections[db].ops.value_to_db_datetime(now)])
adrian 2287 3f0790d500a9 2006-02-24: 71: return cursor.fetchone() is not None
adrian 2287 3f0790d500a9 2006-02-24: 72:
russellm 13275 4dd52d4f3f50 2010-08-05:123: def _cull(self, db, cursor, now):
adrian 2287 3f0790d500a9 2006-02-24: 74: if self._cull_frequency == 0:
russellm 12116 c1ad14c551a9 2010-01-27: 87: self.clear()
adrian 2287 3f0790d500a9 2006-02-24: 76: else:
russellm 13480 7351411944a9 2010-08-31:127: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05:127: cursor.execute("DELETE FROM %s WHERE expires < %%s" % table,
russellm 13275 4dd52d4f3f50 2010-08-05:128: [connections[db].ops.value_to_db_datetime(now)])
russellm 13275 4dd52d4f3f50 2010-08-05:129: cursor.execute("SELECT COUNT(*) FROM %s" % table)
adrian 2287 3f0790d500a9 2006-02-24: 79: num = cursor.fetchone()[0]
adrian 2287 3f0790d500a9 2006-02-24: 80: if num > self._max_entries:
russellm 13275 4dd52d4f3f50 2010-08-05:132: cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % table, [num / self._cull_frequency])
russellm 13275 4dd52d4f3f50 2010-08-05:133: cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % table, [cursor.fetchone()[0]])
russellm 12116 c1ad14c551a9 2010-01-27: 95:
russellm 12116 c1ad14c551a9 2010-01-27: 96: def clear(self):
russellm 13275 4dd52d4f3f50 2010-08-05:136: db = router.db_for_write(self.cache_model_class)
russellm 13275 4dd52d4f3f50 2010-08-05:137: table = connections[db].ops.quote_name(self._table)
russellm 13275 4dd52d4f3f50 2010-08-05:138: cursor = connections[db].cursor()
russellm 13275 4dd52d4f3f50 2010-08-05:139: cursor.execute('DELETE FROM %s' % table)
russellm 14807 905c4a7e1f19 2010-12-21:143:
russellm 14807 905c4a7e1f19 2010-12-21:144: # For backwards compatibility
russellm 14807 905c4a7e1f19 2010-12-21:145: class CacheClass(DatabaseCache):
russellm 14807 905c4a7e1f19 2010-12-21:146: pass