|
0
|
1 |
"Database cache backend." |
|
|
2 |
|
|
|
3 |
from django.core.cache.backends.base import BaseCache |
|
|
4 |
from django.db import connection, transaction, DatabaseError |
|
|
5 |
import base64, time |
|
|
6 |
from datetime import datetime |
|
|
7 |
try: |
|
|
8 |
import cPickle as pickle |
|
|
9 |
except ImportError: |
|
|
10 |
import pickle |
|
|
11 |
|
|
|
12 |
class CacheClass(BaseCache): |
|
|
13 |
def __init__(self, table, params): |
|
|
14 |
BaseCache.__init__(self, params) |
|
29
|
15 |
self._table = connection.ops.quote_name(table) |
|
0
|
16 |
max_entries = params.get('max_entries', 300) |
|
|
17 |
try: |
|
|
18 |
self._max_entries = int(max_entries) |
|
|
19 |
except (ValueError, TypeError): |
|
|
20 |
self._max_entries = 300 |
|
|
21 |
cull_frequency = params.get('cull_frequency', 3) |
|
|
22 |
try: |
|
|
23 |
self._cull_frequency = int(cull_frequency) |
|
|
24 |
except (ValueError, TypeError): |
|
|
25 |
self._cull_frequency = 3 |
|
|
26 |
|
|
|
27 |
def get(self, key, default=None): |
|
|
28 |
cursor = connection.cursor() |
|
|
29 |
cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) |
|
|
30 |
row = cursor.fetchone() |
|
|
31 |
if row is None: |
|
|
32 |
return default |
|
|
33 |
now = datetime.now() |
|
|
34 |
if row[2] < now: |
|
|
35 |
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) |
|
|
36 |
transaction.commit_unless_managed() |
|
|
37 |
return default |
|
|
38 |
value = connection.ops.process_clob(row[1]) |
|
|
39 |
return pickle.loads(base64.decodestring(value)) |
|
|
40 |
|
|
|
41 |
def set(self, key, value, timeout=None): |
|
|
42 |
self._base_set('set', key, value, timeout) |
|
|
43 |
|
|
|
44 |
def add(self, key, value, timeout=None): |
|
|
45 |
return self._base_set('add', key, value, timeout) |
|
|
46 |
|
|
|
47 |
def _base_set(self, mode, key, value, timeout=None): |
|
|
48 |
if timeout is None: |
|
|
49 |
timeout = self.default_timeout |
|
|
50 |
cursor = connection.cursor() |
|
|
51 |
cursor.execute("SELECT COUNT(*) FROM %s" % self._table) |
|
|
52 |
num = cursor.fetchone()[0] |
|
|
53 |
now = datetime.now().replace(microsecond=0) |
|
|
54 |
exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0) |
|
|
55 |
if num > self._max_entries: |
|
|
56 |
self._cull(cursor, now) |
|
|
57 |
encoded = base64.encodestring(pickle.dumps(value, 2)).strip() |
|
|
58 |
cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) |
|
|
59 |
try: |
|
|
60 |
result = cursor.fetchone() |
|
|
61 |
if result and (mode == 'set' or |
|
|
62 |
(mode == 'add' and result[1] < now)): |
|
29
|
63 |
cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, |
|
|
64 |
[encoded, connection.ops.value_to_db_datetime(exp), key]) |
|
0
|
65 |
else: |
|
29
|
66 |
cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, |
|
|
67 |
[key, encoded, connection.ops.value_to_db_datetime(exp)]) |
|
0
|
68 |
except DatabaseError: |
|
|
69 |
# To be threadsafe, updates/inserts are allowed to fail silently |
|
29
|
70 |
transaction.rollback_unless_managed() |
|
0
|
71 |
return False |
|
|
72 |
else: |
|
|
73 |
transaction.commit_unless_managed() |
|
|
74 |
return True |
|
|
75 |
|
|
|
76 |
def delete(self, key): |
|
|
77 |
cursor = connection.cursor() |
|
|
78 |
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) |
|
|
79 |
transaction.commit_unless_managed() |
|
|
80 |
|
|
|
81 |
def has_key(self, key): |
|
|
82 |
now = datetime.now().replace(microsecond=0) |
|
|
83 |
cursor = connection.cursor() |
|
29
|
84 |
cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table, |
|
|
85 |
[key, connection.ops.value_to_db_datetime(now)]) |
|
0
|
86 |
return cursor.fetchone() is not None |
|
|
87 |
|
|
|
88 |
def _cull(self, cursor, now): |
|
|
89 |
if self._cull_frequency == 0: |
|
29
|
90 |
self.clear() |
|
0
|
91 |
else: |
|
29
|
92 |
cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, |
|
|
93 |
[connection.ops.value_to_db_datetime(now)]) |
|
0
|
94 |
cursor.execute("SELECT COUNT(*) FROM %s" % self._table) |
|
|
95 |
num = cursor.fetchone()[0] |
|
|
96 |
if num > self._max_entries: |
|
|
97 |
cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency]) |
|
|
98 |
cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]]) |
|
29
|
99 |
|
|
|
100 |
def clear(self): |
|
|
101 |
cursor = connection.cursor() |
|
|
102 |
cursor.execute('DELETE FROM %s' % self._table) |