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 | from time import time
from threading import Lock
from django.core.cache import cache
from django.utils.cache import patch_response_headers
def single_cacheable(cache_timeout=60, stale_timeout=60, key_template=''):
def paramed_decorator(func):
func.__func_lck = Lock()
def decorated(request, *args, **kw):
lck = func.__func_lck
if cache_timeout != 0 and request.method == "GET":
key = "sc_"+key_template.format(*args, **kw)
val = cache.get(key)
ctout = cache_timeout
if val is None:
acq_ret = lck.acquire(0)
try:
if acq_ret == False: # other thread have lock
lck.acquire(1) # wait while other thread finished
val = cache.get(key) # check for responce cached by other thread
if val is None:
resp = func(request, *args, **kw)
if hasattr(resp, '_cache_timeout'):
ctout = resp._cache_timeout
patch_response_headers(resp, ctout)
else:
refresh_tm, resp = val
return resp
else: # we are first thread what acquired lock
resp = func(request, *args, **kw)
if hasattr(resp, '_cache_timeout'):
ctout = resp._cache_timeout
patch_response_headers(resp, ctout)
cache.set(key, (ctout + time(), resp), ctout + stale_timeout)
finally:
lck.release()
else:
refresh_tm, resp = val
if time() > refresh_tm and refresh_tm > 0:
if lck.acquire(0) == False: # other thread refreshed cache
return resp
try:
cache.set(key, (0, resp), stale_timeout) # mark responce as stale
resp = func(request, *args, **kw) # compute fresh value
if hasattr(resp, '_cache_timeout'):
ctout = resp._cache_timeout
patch_response_headers(resp, ctout)
cache.set(key, (ctout + time(), resp), ctout + stale_timeout)
finally:
lck.release()
else:
return resp
return resp
else:
with lck: # wait while other thread finished
resp = func(request, *args, **kw) # run the view
return resp
decorated.__doc__ = func.__doc__
decorated.__dict__ = func.__dict__
return decorated
return paramed_decorator
|
Comments