blob: 3acc13feeb8158c24f51e076257655ac26a17a05 [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