1 | from gluon.contrib.memcache.memcache import Client |
---|
2 | from gluon.cache import CacheAbstract |
---|
3 | import time |
---|
4 | |
---|
5 | """ |
---|
6 | examle of usage: |
---|
7 | |
---|
8 | cache.memcache = MemcacheClient(request,[127.0.0.1:11211],debug=true) |
---|
9 | """ |
---|
10 | |
---|
11 | import cPickle as pickle |
---|
12 | import thread |
---|
13 | from gluon import current |
---|
14 | |
---|
15 | DEFAULT_TIME_EXPIRE = 300 # seconds (must be the same as cache.ram) |
---|
16 | |
---|
17 | def MemcacheClient(*a, **b): |
---|
18 | if not hasattr(current,'__memcache_client'): |
---|
19 | current.__memcache_client = MemcacheClientObj(*a, **b) |
---|
20 | return current.__memcache_client |
---|
21 | |
---|
22 | class MemcacheClientObj(Client): |
---|
23 | |
---|
24 | def initialize(self): |
---|
25 | pass |
---|
26 | |
---|
27 | meta_storage = {} |
---|
28 | max_time_expire = 24*3600 |
---|
29 | |
---|
30 | def __init__(self, request, servers, debug=0, pickleProtocol=0, |
---|
31 | pickler=pickle.Pickler, unpickler=pickle.Unpickler, |
---|
32 | pload=None, pid=None, |
---|
33 | default_time_expire = DEFAULT_TIME_EXPIRE): |
---|
34 | self.request=request |
---|
35 | self.default_time_expire = default_time_expire |
---|
36 | if request: |
---|
37 | app = request.application |
---|
38 | else: |
---|
39 | app = '' |
---|
40 | Client.__init__(self, servers, debug, pickleProtocol, |
---|
41 | pickler, unpickler, pload, pid) |
---|
42 | if not app in self.meta_storage: |
---|
43 | self.storage = self.meta_storage[app] = { |
---|
44 | CacheAbstract.cache_stats_name: { |
---|
45 | 'hit_total': 0, |
---|
46 | 'misses': 0, |
---|
47 | }} |
---|
48 | else: |
---|
49 | self.storage = self.meta_storage[app] |
---|
50 | |
---|
51 | def __call__(self, key, f, time_expire = 'default'): |
---|
52 | if time_expire == 'default': |
---|
53 | time_expire = self.default_time_expire |
---|
54 | if time_expire == None: |
---|
55 | time_expire = self.max_time_expire |
---|
56 | # this must be commented because get and set are redefined |
---|
57 | # key = self.__keyFormat__(key) |
---|
58 | now = time.time() |
---|
59 | value = None |
---|
60 | if f is None: # force deletion of value |
---|
61 | self.delete(key) |
---|
62 | return None |
---|
63 | elif time_expire==0: # value forced expired |
---|
64 | item = None # value to be computed |
---|
65 | else: |
---|
66 | item = self.get(key) |
---|
67 | if item: |
---|
68 | if not isinstance(item,(list,tuple)): |
---|
69 | value = item |
---|
70 | elif (item[0] < now - time_expire): # value expired |
---|
71 | item = None # value to be computed |
---|
72 | else: |
---|
73 | value = item[1] |
---|
74 | if not item: |
---|
75 | value = f() |
---|
76 | self.set(key, (now,value), self.max_time_expire) |
---|
77 | return value |
---|
78 | |
---|
79 | def increment(self, key, value=1, time_expire='default'): |
---|
80 | """ time_expire is ignored """ |
---|
81 | if time_expire == 'default': |
---|
82 | time_expire = self.default_time_expire |
---|
83 | newKey = self.__keyFormat__(key) |
---|
84 | obj = Client.get(self, newKey) |
---|
85 | if obj: |
---|
86 | if isinstance(obj,(int,float,long)): |
---|
87 | return Client.incr(self, newKey, value) |
---|
88 | else: |
---|
89 | value += obj[1] |
---|
90 | Client.set(self,newKey,(time.time(),value), |
---|
91 | self.max_time_expire) |
---|
92 | return value |
---|
93 | else: |
---|
94 | Client.set(self, newKey, value, self.max_time_expire) |
---|
95 | return value |
---|
96 | |
---|
97 | def set(self, key, value, time_expire='default'): |
---|
98 | if time_expire == 'default': |
---|
99 | time_expire = self.default_time_expire |
---|
100 | newKey = self.__keyFormat__(key) |
---|
101 | return Client.set(self, newKey, value, time_expire) |
---|
102 | |
---|
103 | def get(self, key): |
---|
104 | newKey = self.__keyFormat__(key) |
---|
105 | return Client.get(self, newKey) |
---|
106 | |
---|
107 | def delete(self, key): |
---|
108 | newKey = self.__keyFormat__(key) |
---|
109 | return Client.delete(self, newKey) |
---|
110 | |
---|
111 | def __keyFormat__(self, key): |
---|
112 | return '%s/%s' % (self.request.application, key.replace(' ', '_')) |
---|
113 | |
---|
114 | |
---|