##// END OF EJS Templates
cache: updated cache decorators based on latest code from dogpile
marcink -
r751:fb55dc24 default
parent child Browse files
Show More
@@ -1,151 +1,153 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2019 RhodeCode GmbH
2 # Copyright (C) 2014-2019 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import os
18 import os
19 import logging
19 import logging
20 import functools
20 import functools
21 from decorator import decorate
21
22
22 from dogpile.cache import CacheRegion
23 from dogpile.cache import CacheRegion
23 from dogpile.cache.util import compat
24 from dogpile.cache.util import compat
24
25
25 from vcsserver.utils import safe_str, sha1
26 from vcsserver.utils import safe_str, sha1
26
27
27
28
28 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
29
30
30
31
31 class RhodeCodeCacheRegion(CacheRegion):
32 class RhodeCodeCacheRegion(CacheRegion):
32
33
33 def conditional_cache_on_arguments(
34 def conditional_cache_on_arguments(
34 self, namespace=None,
35 self, namespace=None,
35 expiration_time=None,
36 expiration_time=None,
36 should_cache_fn=None,
37 should_cache_fn=None,
37 to_str=compat.string_type,
38 to_str=compat.string_type,
38 function_key_generator=None,
39 function_key_generator=None,
39 condition=True):
40 condition=True):
40 """
41 """
41 Custom conditional decorator, that will not touch any dogpile internals if
42 Custom conditional decorator, that will not touch any dogpile internals if
42 condition isn't meet. This works a bit different than should_cache_fn
43 condition isn't meet. This works a bit different than should_cache_fn
43 And it's faster in cases we don't ever want to compute cached values
44 And it's faster in cases we don't ever want to compute cached values
44 """
45 """
45 expiration_time_is_callable = compat.callable(expiration_time)
46 expiration_time_is_callable = compat.callable(expiration_time)
46
47
47 if function_key_generator is None:
48 if function_key_generator is None:
48 function_key_generator = self.function_key_generator
49 function_key_generator = self.function_key_generator
49
50
50 def decorator(fn):
51 def get_or_create_for_user_func(key_generator, user_func, *arg, **kw):
51 if to_str is compat.string_type:
52 # backwards compatible
53 key_generator = function_key_generator(namespace, fn)
54 else:
55 key_generator = function_key_generator(namespace, fn, to_str=to_str)
56
57 @functools.wraps(fn)
58 def decorate(*arg, **kw):
59 key = key_generator(*arg, **kw)
60
61 @functools.wraps(fn)
62 def creator():
63 return fn(*arg, **kw)
64
52
65 if not condition:
53 if not condition:
66 log.debug('Calling un-cached func:%s', fn)
54 log.debug('Calling un-cached func:%s', user_func)
67 return creator()
55 return user_func(*arg, **kw)
56
57 key = key_generator(*arg, **kw)
68
58
69 timeout = expiration_time() if expiration_time_is_callable \
59 timeout = expiration_time() if expiration_time_is_callable \
70 else expiration_time
60 else expiration_time
71
61
72 log.debug('Calling cached fn:%s', fn)
62 log.debug('Calling cached fn:%s', user_func)
73 return self.get_or_create(key, creator, timeout, should_cache_fn)
63 return self.get_or_create(key, user_func, timeout, should_cache_fn, (arg, kw))
64
65 def cache_decorator(user_func):
66 if to_str is compat.string_type:
67 # backwards compatible
68 key_generator = function_key_generator(namespace, user_func)
69 else:
70 key_generator = function_key_generator(namespace, user_func, to_str=to_str)
71
72 def refresh(*arg, **kw):
73 """
74 Like invalidate, but regenerates the value instead
75 """
76 key = key_generator(*arg, **kw)
77 value = user_func(*arg, **kw)
78 self.set(key, value)
79 return value
74
80
75 def invalidate(*arg, **kw):
81 def invalidate(*arg, **kw):
76 key = key_generator(*arg, **kw)
82 key = key_generator(*arg, **kw)
77 self.delete(key)
83 self.delete(key)
78
84
79 def set_(value, *arg, **kw):
85 def set_(value, *arg, **kw):
80 key = key_generator(*arg, **kw)
86 key = key_generator(*arg, **kw)
81 self.set(key, value)
87 self.set(key, value)
82
88
83 def get(*arg, **kw):
89 def get(*arg, **kw):
84 key = key_generator(*arg, **kw)
90 key = key_generator(*arg, **kw)
85 return self.get(key)
91 return self.get(key)
86
92
87 def refresh(*arg, **kw):
93 user_func.set = set_
88 key = key_generator(*arg, **kw)
94 user_func.invalidate = invalidate
89 value = fn(*arg, **kw)
95 user_func.get = get
90 self.set(key, value)
96 user_func.refresh = refresh
91 return value
97 user_func.key_generator = key_generator
98 user_func.original = user_func
92
99
93 decorate.set = set_
100 # Use `decorate` to preserve the signature of :param:`user_func`.
94 decorate.invalidate = invalidate
95 decorate.refresh = refresh
96 decorate.get = get
97 decorate.original = fn
98 decorate.key_generator = key_generator
99 decorate.__wrapped__ = fn
100
101
101 return decorate
102 return decorate(user_func, functools.partial(
103 get_or_create_for_user_func, key_generator))
102
104
103 return decorator
105 return cache_decorator
104
106
105
107
106 def make_region(*arg, **kw):
108 def make_region(*arg, **kw):
107 return RhodeCodeCacheRegion(*arg, **kw)
109 return RhodeCodeCacheRegion(*arg, **kw)
108
110
109
111
110 def get_default_cache_settings(settings, prefixes=None):
112 def get_default_cache_settings(settings, prefixes=None):
111 prefixes = prefixes or []
113 prefixes = prefixes or []
112 cache_settings = {}
114 cache_settings = {}
113 for key in settings.keys():
115 for key in settings.keys():
114 for prefix in prefixes:
116 for prefix in prefixes:
115 if key.startswith(prefix):
117 if key.startswith(prefix):
116 name = key.split(prefix)[1].strip()
118 name = key.split(prefix)[1].strip()
117 val = settings[key]
119 val = settings[key]
118 if isinstance(val, compat.string_types):
120 if isinstance(val, compat.string_types):
119 val = val.strip()
121 val = val.strip()
120 cache_settings[name] = val
122 cache_settings[name] = val
121 return cache_settings
123 return cache_settings
122
124
123
125
124 def compute_key_from_params(*args):
126 def compute_key_from_params(*args):
125 """
127 """
126 Helper to compute key from given params to be used in cache manager
128 Helper to compute key from given params to be used in cache manager
127 """
129 """
128 return sha1("_".join(map(safe_str, args)))
130 return sha1("_".join(map(safe_str, args)))
129
131
130
132
131 def backend_key_generator(backend):
133 def backend_key_generator(backend):
132 """
134 """
133 Special wrapper that also sends over the backend to the key generator
135 Special wrapper that also sends over the backend to the key generator
134 """
136 """
135 def wrapper(namespace, fn):
137 def wrapper(namespace, fn):
136 return key_generator(backend, namespace, fn)
138 return key_generator(backend, namespace, fn)
137 return wrapper
139 return wrapper
138
140
139
141
140 def key_generator(backend, namespace, fn):
142 def key_generator(backend, namespace, fn):
141 fname = fn.__name__
143 fname = fn.__name__
142
144
143 def generate_key(*args):
145 def generate_key(*args):
144 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
146 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
145 namespace_pref = namespace or 'default_namespace'
147 namespace_pref = namespace or 'default_namespace'
146 arg_key = compute_key_from_params(*args)
148 arg_key = compute_key_from_params(*args)
147 final_key = "{}:{}:{}_{}".format(backend_prefix, namespace_pref, fname, arg_key)
149 final_key = "{}:{}:{}_{}".format(backend_prefix, namespace_pref, fname, arg_key)
148
150
149 return final_key
151 return final_key
150
152
151 return generate_key
153 return generate_key
General Comments 0
You need to be logged in to leave comments. Login now