Run and cache only one instance of a heavy request

 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

More like this

  1. Effective content caching for mass-load site using redirect feature by nnseva 1 year, 10 months ago
  2. cache_smart template tag by michiel_1981 5 years, 3 months ago
  3. Soft Timeout Middleware by adamlofts 3 years, 11 months ago
  4. integrated jinja2 which could use generic view ,my djangojinja2.py by jasongreen 3 years, 4 months ago
  5. MintCache by gfranxman 6 years, 1 month ago

Comments

(Forgotten your password?)