-
Notifications
You must be signed in to change notification settings - Fork 3
/
cache.py
200 lines (163 loc) · 6.75 KB
/
cache.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from twisted.python import log
from twisted.protocols.memcache import MemCacheProtocol
from twisted.internet import protocol, reactor
import random, time, hashlib, mail, traceback
try:
import cPickle as pickle
except ImportError:
log.msg('cPickle not available, using slower pickle library.')
import pickle
class Cache:
def __init__(self, config):
self.config = config
def ready(self):
"Call when the cache is online"
pass
def set(self, dictionary, time = None):
"Store value(s) supplied as a python dict for a certain time"
pass
def get(self, keylist):
"Retreive a list of values as a python dict"
return {}
class InternalCache(Cache):
def __init__(self, config):
Cache.__init__(self, config)
self.cache = {}
self.ready()
def ready(self):
limit = self.config.get('memory_limit')
if not limit:
log.msg('WARNING: memory_limit not specified, using 100MB as default')
log.msg("CACHE_BACKEND: Using %s MB in-memory cache" % limit)
def set(self, dictionary, time = None):
for key, val in dict(dictionary.items()):
element = {
'expires_on' : time.time() + (time or 0),
'element' : val
}
dictionary[key] = element
self.cache.update(element)
def get(self, keylist):
if not isinstance(keylist, list): keylist = [keylist]
output = {}
for key in keylist:
element = self.cache.get(key)
if element:
if time.time() > element['expires_on']:
output[key] = None
else:
output[key] = element['element']
return output
def delete(self, keylist):
for key in keylist:
try:
del self.cache[key]
except:
pass
def flush(self):
self.cache = {}
class PythonmemcacheCache(Cache):
def __init__(self, config):
Cache.__init__(self, config)
servers = config['cache_server'].split(',')
pool_size = int(config.get('cache_pool', 1))
reactor.suggestThreadPoolSize(500)
log.msg('Creating memcache connections to servers %s...' % ','.join(servers))
try:
import mc
except:
log.msg('Failed to import memcache helper library!')
log.msg(traceback.format_exc())
return
try:
self.mc = mc.Mc(servers)
self.ready(servers)
except:
log.msg('Failed ot create memcache object!')
def ready(self, servers):
log.msg("CACHE_BACKEND: Connected to memcache servers %s" % ','.join(servers))
def set(self, dictionary, time = None):
"Set all values that are not None"
pickled_dict = dict([(hashlib.md5(key).hexdigest(), pickle.dumps(val)) for key, val in dictionary.items() if val is not None])
if len(pickled_dict):
return self.mc.set_multi(pickled_dict, time)
else:
return {}
def get(self, keylist):
'Get values'
if not isinstance(keylist, list): keylist = [keylist]
md5list = [hashlib.md5(key).hexdigest() for key in keylist]
return self.mc.get_multi(md5list).addCallback(self._format, keylist, md5list)
def delete(self, keylist):
if not isinstance(keylist, list): keylist = [keylist]
md5list = [hashlib.md5(key).hexdigest() for key in keylist]
self.mc.delete_multi(md5list)
def _format(self, results, keylist, md5list):
"Return a dictionary containing all keys in keylist, with cache misses as None"
output = dict([(key, results.get(md5) and pickle.loads(results[md5])) for key, md5 in zip(keylist, md5list)])
#log.msg('Memcache results:\n%s' % repr(output))
return output
def flush(self):
log.msg('ERROR: Unsupport operation flush() on PythonMemcacheCache')
class MemcacheCache(Cache):
def __init__(self, config):
Cache.__init__(self, config)
server = config['cache_server'].split(',')[0]
connection_pool_size = int(config.get('cache_pool', 1))
log.msg('Creating memcache connection pool to server %s...' % server)
self.pool = []
try:
self.host, self.port = server.split(':')
except:
self.host = server
self.port = 11211
for i in xrange(connection_pool_size):
d = protocol.ClientCreator(reactor, MemCacheProtocol).connectTCP(self.host, int(self.port))
d.addCallback(self.ready)
def ready(self, result=None):
log.msg("CACHE_BACKEND: Connected to memcache server at %s:%s" % (self.host, self.port))
self.pool.append(result)
def cache_pool(self):
return random.choice(self.pool)
def set(self, dictionary, time = None):
"Set all values that are not None"
pickled_dict = dict([(hashlib.md5(key).hexdigest(), pickle.dumps(val)) for key, val in dictionary.items() if val is not None])
cache = self.cache_pool()
#log.msg('SET on cache %s' % cache)
if len(pickled_dict):
return cache.set_multi(pickled_dict, expireTime = time)
else:
return {}
def get(self, keylist):
'Get values'
if not isinstance(keylist, list): keylist = [keylist]
md5list = [hashlib.md5(key).hexdigest() for key in keylist]
#log.msg('keylist: %s' % keylist)
cache = self.cache_pool()
#log.msg('GET on cache %s' % cache)
return cache.get_multi(md5list).addCallback(self._format, keylist, md5list)
def delete(self, keylist):
for key in keylist:
self.cache_pool().delete(hashlib.md5(key).hexdigest())
def _format(self, results, keylist, md5list):
"Return a dictionary containing all keys in keylist, with cache misses as None"
output = dict([(key, results[1].get(md5) and pickle.loads(results[1][md5])) for key, md5 in zip(keylist, md5list)])
#log.msg('Memcache results:\n%s' % repr(output))
return output
def flush(self):
self.cache_pool().flushAll()
class NullCache(Cache):
def __init__(self, config):
Cache.__init__(self, config)
self.ready()
def ready(self):
log.msg("CACHE_BACKEND: Using NULL cache (Nothing will be cached)")
def set(self, dictionary, time = None):
pass
def get(self, keylist):
if not isinstance(keylist, list): keylist = [keylist]
return dict([[key, None] for key in keylist])
def delete(self, keylist):
pass
def flush(self):
pass