##// END OF EJS Templates
vcsserver: logging improvements
super-admin -
r991:aaa8cc34 default
parent child Browse files
Show More
@@ -1,263 +1,263 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-2020 RhodeCode GmbH
2 # Copyright (C) 2014-2020 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 time
19 import time
20 import logging
20 import logging
21 import functools
21 import functools
22
22
23 from dogpile.cache import CacheRegion
23 from dogpile.cache import CacheRegion
24 from dogpile.cache.util import compat
24 from dogpile.cache.util import compat
25
25
26 from vcsserver.utils import safe_str, sha1
26 from vcsserver.utils import safe_str, sha1
27
27
28 from vcsserver.lib.rc_cache import region_meta
28 from vcsserver.lib.rc_cache import region_meta
29
29
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32
32
33 class RhodeCodeCacheRegion(CacheRegion):
33 class RhodeCodeCacheRegion(CacheRegion):
34
34
35 def conditional_cache_on_arguments(
35 def conditional_cache_on_arguments(
36 self, namespace=None,
36 self, namespace=None,
37 expiration_time=None,
37 expiration_time=None,
38 should_cache_fn=None,
38 should_cache_fn=None,
39 to_str=compat.string_type,
39 to_str=compat.string_type,
40 function_key_generator=None,
40 function_key_generator=None,
41 condition=True):
41 condition=True):
42 """
42 """
43 Custom conditional decorator, that will not touch any dogpile internals if
43 Custom conditional decorator, that will not touch any dogpile internals if
44 condition isn't meet. This works a bit different than should_cache_fn
44 condition isn't meet. This works a bit different than should_cache_fn
45 And it's faster in cases we don't ever want to compute cached values
45 And it's faster in cases we don't ever want to compute cached values
46 """
46 """
47 expiration_time_is_callable = compat.callable(expiration_time)
47 expiration_time_is_callable = compat.callable(expiration_time)
48
48
49 if function_key_generator is None:
49 if function_key_generator is None:
50 function_key_generator = self.function_key_generator
50 function_key_generator = self.function_key_generator
51
51
52 # workaround for py2 and cython problems, this block should be removed
52 # workaround for py2 and cython problems, this block should be removed
53 # once we've migrated to py3
53 # once we've migrated to py3
54 if 'cython' == 'cython':
54 if 'cython' == 'cython':
55 def decorator(fn):
55 def decorator(fn):
56 if to_str is compat.string_type:
56 if to_str is compat.string_type:
57 # backwards compatible
57 # backwards compatible
58 key_generator = function_key_generator(namespace, fn)
58 key_generator = function_key_generator(namespace, fn)
59 else:
59 else:
60 key_generator = function_key_generator(namespace, fn, to_str=to_str)
60 key_generator = function_key_generator(namespace, fn, to_str=to_str)
61
61
62 @functools.wraps(fn)
62 @functools.wraps(fn)
63 def decorate(*arg, **kw):
63 def decorate(*arg, **kw):
64 key = key_generator(*arg, **kw)
64 key = key_generator(*arg, **kw)
65
65
66 @functools.wraps(fn)
66 @functools.wraps(fn)
67 def creator():
67 def creator():
68 return fn(*arg, **kw)
68 return fn(*arg, **kw)
69
69
70 if not condition:
70 if not condition:
71 return creator()
71 return creator()
72
72
73 timeout = expiration_time() if expiration_time_is_callable \
73 timeout = expiration_time() if expiration_time_is_callable \
74 else expiration_time
74 else expiration_time
75
75
76 return self.get_or_create(key, creator, timeout, should_cache_fn)
76 return self.get_or_create(key, creator, timeout, should_cache_fn)
77
77
78 def invalidate(*arg, **kw):
78 def invalidate(*arg, **kw):
79 key = key_generator(*arg, **kw)
79 key = key_generator(*arg, **kw)
80 self.delete(key)
80 self.delete(key)
81
81
82 def set_(value, *arg, **kw):
82 def set_(value, *arg, **kw):
83 key = key_generator(*arg, **kw)
83 key = key_generator(*arg, **kw)
84 self.set(key, value)
84 self.set(key, value)
85
85
86 def get(*arg, **kw):
86 def get(*arg, **kw):
87 key = key_generator(*arg, **kw)
87 key = key_generator(*arg, **kw)
88 return self.get(key)
88 return self.get(key)
89
89
90 def refresh(*arg, **kw):
90 def refresh(*arg, **kw):
91 key = key_generator(*arg, **kw)
91 key = key_generator(*arg, **kw)
92 value = fn(*arg, **kw)
92 value = fn(*arg, **kw)
93 self.set(key, value)
93 self.set(key, value)
94 return value
94 return value
95
95
96 decorate.set = set_
96 decorate.set = set_
97 decorate.invalidate = invalidate
97 decorate.invalidate = invalidate
98 decorate.refresh = refresh
98 decorate.refresh = refresh
99 decorate.get = get
99 decorate.get = get
100 decorate.original = fn
100 decorate.original = fn
101 decorate.key_generator = key_generator
101 decorate.key_generator = key_generator
102 decorate.__wrapped__ = fn
102 decorate.__wrapped__ = fn
103
103
104 return decorate
104 return decorate
105 return decorator
105 return decorator
106
106
107 def get_or_create_for_user_func(key_generator, user_func, *arg, **kw):
107 def get_or_create_for_user_func(key_generator, user_func, *arg, **kw):
108
108
109 if not condition:
109 if not condition:
110 log.debug('Calling un-cached func:%s', user_func.func_name)
110 log.debug('Calling un-cached method:%s', user_func.func_name)
111 start = time.time()
111 start = time.time()
112 result = user_func(*arg, **kw)
112 result = user_func(*arg, **kw)
113 total = time.time() - start
113 total = time.time() - start
114 log.debug('un-cached func:%s took %.4fs', user_func.func_name, total)
114 log.debug('un-cached method:%s took %.4fs', user_func.func_name, total)
115 return result
115 return result
116
116
117 key = key_generator(*arg, **kw)
117 key = key_generator(*arg, **kw)
118
118
119 timeout = expiration_time() if expiration_time_is_callable \
119 timeout = expiration_time() if expiration_time_is_callable \
120 else expiration_time
120 else expiration_time
121
121
122 log.debug('Calling cached fn:%s', user_func.func_name)
122 log.debug('Calling cached method:`%s`', user_func.func_name)
123 return self.get_or_create(key, user_func, timeout, should_cache_fn, (arg, kw))
123 return self.get_or_create(key, user_func, timeout, should_cache_fn, (arg, kw))
124
124
125 def cache_decorator(user_func):
125 def cache_decorator(user_func):
126 if to_str is compat.string_type:
126 if to_str is compat.string_type:
127 # backwards compatible
127 # backwards compatible
128 key_generator = function_key_generator(namespace, user_func)
128 key_generator = function_key_generator(namespace, user_func)
129 else:
129 else:
130 key_generator = function_key_generator(namespace, user_func, to_str=to_str)
130 key_generator = function_key_generator(namespace, user_func, to_str=to_str)
131
131
132 def refresh(*arg, **kw):
132 def refresh(*arg, **kw):
133 """
133 """
134 Like invalidate, but regenerates the value instead
134 Like invalidate, but regenerates the value instead
135 """
135 """
136 key = key_generator(*arg, **kw)
136 key = key_generator(*arg, **kw)
137 value = user_func(*arg, **kw)
137 value = user_func(*arg, **kw)
138 self.set(key, value)
138 self.set(key, value)
139 return value
139 return value
140
140
141 def invalidate(*arg, **kw):
141 def invalidate(*arg, **kw):
142 key = key_generator(*arg, **kw)
142 key = key_generator(*arg, **kw)
143 self.delete(key)
143 self.delete(key)
144
144
145 def set_(value, *arg, **kw):
145 def set_(value, *arg, **kw):
146 key = key_generator(*arg, **kw)
146 key = key_generator(*arg, **kw)
147 self.set(key, value)
147 self.set(key, value)
148
148
149 def get(*arg, **kw):
149 def get(*arg, **kw):
150 key = key_generator(*arg, **kw)
150 key = key_generator(*arg, **kw)
151 return self.get(key)
151 return self.get(key)
152
152
153 user_func.set = set_
153 user_func.set = set_
154 user_func.invalidate = invalidate
154 user_func.invalidate = invalidate
155 user_func.get = get
155 user_func.get = get
156 user_func.refresh = refresh
156 user_func.refresh = refresh
157 user_func.key_generator = key_generator
157 user_func.key_generator = key_generator
158 user_func.original = user_func
158 user_func.original = user_func
159
159
160 # Use `decorate` to preserve the signature of :param:`user_func`.
160 # Use `decorate` to preserve the signature of :param:`user_func`.
161 return decorator.decorate(user_func, functools.partial(
161 return decorator.decorate(user_func, functools.partial(
162 get_or_create_for_user_func, key_generator))
162 get_or_create_for_user_func, key_generator))
163
163
164 return cache_decorator
164 return cache_decorator
165
165
166
166
167 def make_region(*arg, **kw):
167 def make_region(*arg, **kw):
168 return RhodeCodeCacheRegion(*arg, **kw)
168 return RhodeCodeCacheRegion(*arg, **kw)
169
169
170
170
171 def get_default_cache_settings(settings, prefixes=None):
171 def get_default_cache_settings(settings, prefixes=None):
172 prefixes = prefixes or []
172 prefixes = prefixes or []
173 cache_settings = {}
173 cache_settings = {}
174 for key in settings.keys():
174 for key in settings.keys():
175 for prefix in prefixes:
175 for prefix in prefixes:
176 if key.startswith(prefix):
176 if key.startswith(prefix):
177 name = key.split(prefix)[1].strip()
177 name = key.split(prefix)[1].strip()
178 val = settings[key]
178 val = settings[key]
179 if isinstance(val, compat.string_types):
179 if isinstance(val, compat.string_types):
180 val = val.strip()
180 val = val.strip()
181 cache_settings[name] = val
181 cache_settings[name] = val
182 return cache_settings
182 return cache_settings
183
183
184
184
185 def compute_key_from_params(*args):
185 def compute_key_from_params(*args):
186 """
186 """
187 Helper to compute key from given params to be used in cache manager
187 Helper to compute key from given params to be used in cache manager
188 """
188 """
189 return sha1("_".join(map(safe_str, args)))
189 return sha1("_".join(map(safe_str, args)))
190
190
191
191
192 def backend_key_generator(backend):
192 def backend_key_generator(backend):
193 """
193 """
194 Special wrapper that also sends over the backend to the key generator
194 Special wrapper that also sends over the backend to the key generator
195 """
195 """
196 def wrapper(namespace, fn):
196 def wrapper(namespace, fn):
197 return key_generator(backend, namespace, fn)
197 return key_generator(backend, namespace, fn)
198 return wrapper
198 return wrapper
199
199
200
200
201 def key_generator(backend, namespace, fn):
201 def key_generator(backend, namespace, fn):
202 fname = fn.__name__
202 fname = fn.__name__
203
203
204 def generate_key(*args):
204 def generate_key(*args):
205 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
205 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
206 namespace_pref = namespace or 'default_namespace'
206 namespace_pref = namespace or 'default_namespace'
207 arg_key = compute_key_from_params(*args)
207 arg_key = compute_key_from_params(*args)
208 final_key = "{}:{}:{}_{}".format(backend_prefix, namespace_pref, fname, arg_key)
208 final_key = "{}:{}:{}_{}".format(backend_prefix, namespace_pref, fname, arg_key)
209
209
210 return final_key
210 return final_key
211
211
212 return generate_key
212 return generate_key
213
213
214
214
215 def get_or_create_region(region_name, region_namespace=None):
215 def get_or_create_region(region_name, region_namespace=None):
216 from vcsserver.lib.rc_cache.backends import FileNamespaceBackend
216 from vcsserver.lib.rc_cache.backends import FileNamespaceBackend
217 region_obj = region_meta.dogpile_cache_regions.get(region_name)
217 region_obj = region_meta.dogpile_cache_regions.get(region_name)
218 if not region_obj:
218 if not region_obj:
219 raise EnvironmentError(
219 raise EnvironmentError(
220 'Region `{}` not in configured: {}.'.format(
220 'Region `{}` not in configured: {}.'.format(
221 region_name, region_meta.dogpile_cache_regions.keys()))
221 region_name, region_meta.dogpile_cache_regions.keys()))
222
222
223 region_uid_name = '{}:{}'.format(region_name, region_namespace)
223 region_uid_name = '{}:{}'.format(region_name, region_namespace)
224 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
224 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
225 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
225 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
226 if region_exist:
226 if region_exist:
227 log.debug('Using already configured region: %s', region_namespace)
227 log.debug('Using already configured region: %s', region_namespace)
228 return region_exist
228 return region_exist
229 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
229 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
230 expiration_time = region_obj.expiration_time
230 expiration_time = region_obj.expiration_time
231
231
232 if not os.path.isdir(cache_dir):
232 if not os.path.isdir(cache_dir):
233 os.makedirs(cache_dir)
233 os.makedirs(cache_dir)
234 new_region = make_region(
234 new_region = make_region(
235 name=region_uid_name,
235 name=region_uid_name,
236 function_key_generator=backend_key_generator(region_obj.actual_backend)
236 function_key_generator=backend_key_generator(region_obj.actual_backend)
237 )
237 )
238 namespace_filename = os.path.join(
238 namespace_filename = os.path.join(
239 cache_dir, "{}.cache.dbm".format(region_namespace))
239 cache_dir, "{}.cache.dbm".format(region_namespace))
240 # special type that allows 1db per namespace
240 # special type that allows 1db per namespace
241 new_region.configure(
241 new_region.configure(
242 backend='dogpile.cache.rc.file_namespace',
242 backend='dogpile.cache.rc.file_namespace',
243 expiration_time=expiration_time,
243 expiration_time=expiration_time,
244 arguments={"filename": namespace_filename}
244 arguments={"filename": namespace_filename}
245 )
245 )
246
246
247 # create and save in region caches
247 # create and save in region caches
248 log.debug('configuring new region: %s', region_uid_name)
248 log.debug('configuring new region: %s', region_uid_name)
249 region_obj = region_meta.dogpile_cache_regions[region_namespace] = new_region
249 region_obj = region_meta.dogpile_cache_regions[region_namespace] = new_region
250
250
251 return region_obj
251 return region_obj
252
252
253
253
254 def clear_cache_namespace(cache_region, cache_namespace_uid, invalidate=False):
254 def clear_cache_namespace(cache_region, cache_namespace_uid, invalidate=False):
255 region = get_or_create_region(cache_region, cache_namespace_uid)
255 region = get_or_create_region(cache_region, cache_namespace_uid)
256 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
256 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
257 num_delete_keys = len(cache_keys)
257 num_delete_keys = len(cache_keys)
258 if invalidate:
258 if invalidate:
259 region.invalidate(hard=False)
259 region.invalidate(hard=False)
260 else:
260 else:
261 if num_delete_keys:
261 if num_delete_keys:
262 region.delete_multi(cache_keys)
262 region.delete_multi(cache_keys)
263 return num_delete_keys
263 return num_delete_keys
General Comments 0
You need to be logged in to leave comments. Login now