##// END OF EJS Templates
caches: synced with CE code
super-admin -
r1113:dcc620cb python3
parent child Browse files
Show More
@@ -1,3 +1,5 b''
1 # -*- coding: utf-8 -*-
2
1 # RhodeCode VCSServer provides access to different vcs backends via network.
3 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2020 RhodeCode GmbH
4 # Copyright (C) 2014-2020 RhodeCode GmbH
3 #
5 #
@@ -16,9 +18,20 b''
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
19
18 import logging
20 import logging
21 import threading
19
22
20 from dogpile.cache import register_backend
23 from dogpile.cache import register_backend
21
24
25 from . import region_meta
26 from .utils import (
27 backend_key_generator,
28 clear_cache_namespace,
29 get_default_cache_settings,
30 get_or_create_region,
31 make_region,
32 str2bool,
33 )
34
22 module_name = 'vcsserver'
35 module_name = 'vcsserver'
23
36
24 register_backend(
37 register_backend(
@@ -40,14 +53,18 b' register_backend('
40
53
41 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
42
55
43 from . import region_meta
56
44 from .utils import (
57 def async_creation_runner(cache, somekey, creator, mutex):
45 backend_key_generator,
58
46 clear_cache_namespace,
59 def runner():
47 get_default_cache_settings,
60 try:
48 get_or_create_region,
61 value = creator()
49 make_region,
62 cache.set(somekey, value)
50 )
63 finally:
64 mutex.release()
65
66 thread = threading.Thread(target=runner)
67 thread.start()
51
68
52
69
53 def configure_dogpile_cache(settings):
70 def configure_dogpile_cache(settings):
@@ -69,13 +86,20 b' def configure_dogpile_cache(settings):'
69
86
70 new_region = make_region(
87 new_region = make_region(
71 name=namespace_name,
88 name=namespace_name,
72 function_key_generator=None
89 function_key_generator=None,
90 async_creation_runner=None
73 )
91 )
74
92
75 new_region.configure_from_config(settings, 'rc_cache.{}.'.format(namespace_name))
93 new_region.configure_from_config(settings, f'rc_cache.{namespace_name}.')
76 new_region.function_key_generator = backend_key_generator(new_region.actual_backend)
94 new_region.function_key_generator = backend_key_generator(new_region.actual_backend)
95
96 async_creator = str2bool(settings.pop(f'rc_cache.{namespace_name}.async_creator', 'false'))
97 if async_creator:
98 log.debug('configuring region %s with async creator', new_region)
99 new_region.async_creation_runner = async_creation_runner
100
77 if log.isEnabledFor(logging.DEBUG):
101 if log.isEnabledFor(logging.DEBUG):
78 region_args = dict(backend=new_region.actual_backend.__class__,
102 region_args = dict(backend=new_region.actual_backend,
79 region_invalidator=new_region.region_invalidator.__class__)
103 region_invalidator=new_region.region_invalidator.__class__)
80 log.debug('dogpile: registering a new region `%s` %s', namespace_name, region_args)
104 log.debug('dogpile: registering a new region `%s` %s', namespace_name, region_args)
81
105
@@ -19,9 +19,11 b' import errno'
19 import fcntl
19 import fcntl
20 import functools
20 import functools
21 import logging
21 import logging
22 import os
22 import pickle
23 import pickle
23 import time
24 #import time
24
25
26 #import gevent
25 import msgpack
27 import msgpack
26 import redis
28 import redis
27
29
@@ -34,10 +36,10 b' from dogpile.cache.backends import memor'
34 from dogpile.cache.backends import redis as redis_backend
36 from dogpile.cache.backends import redis as redis_backend
35 from dogpile.cache.backends.file import FileLock
37 from dogpile.cache.backends.file import FileLock
36 from dogpile.cache.util import memoized_property
38 from dogpile.cache.util import memoized_property
37 from pyramid.settings import asbool
38
39
39 from vcsserver.lib.memory_lru_dict import LRUDict, LRUDictDebug
40 from vcsserver.lib.memory_lru_dict import LRUDict, LRUDictDebug
40 from vcsserver.str_utils import safe_bytes, safe_str
41 from vcsserver.str_utils import safe_bytes, safe_str
42 from vcsserver.type_utils import str2bool
41
43
42 _default_max_size = 1024
44 _default_max_size = 1024
43
45
@@ -49,15 +51,21 b' class LRUMemoryBackend(memory_backend.Me'
49 pickle_values = False
51 pickle_values = False
50
52
51 def __init__(self, arguments):
53 def __init__(self, arguments):
52 max_size = arguments.pop('max_size', _default_max_size)
54 self.max_size = arguments.pop('max_size', _default_max_size)
53
55
54 LRUDictClass = LRUDict
56 LRUDictClass = LRUDict
55 if arguments.pop('log_key_count', None):
57 if arguments.pop('log_key_count', None):
56 LRUDictClass = LRUDictDebug
58 LRUDictClass = LRUDictDebug
57
59
58 arguments['cache_dict'] = LRUDictClass(max_size)
60 arguments['cache_dict'] = LRUDictClass(self.max_size)
59 super(LRUMemoryBackend, self).__init__(arguments)
61 super(LRUMemoryBackend, self).__init__(arguments)
60
62
63 def __repr__(self):
64 return f'{self.__class__}(maxsize=`{self.max_size}`)'
65
66 def __str__(self):
67 return self.__repr__()
68
61 def delete(self, key):
69 def delete(self, key):
62 try:
70 try:
63 del self._cache[key]
71 del self._cache[key]
@@ -100,7 +108,11 b' class FileNamespaceBackend(PickleSeriali'
100 arguments['lock_factory'] = CustomLockFactory
108 arguments['lock_factory'] = CustomLockFactory
101 db_file = arguments.get('filename')
109 db_file = arguments.get('filename')
102
110
103 log.debug('initialing %s DB in %s', self.__class__.__name__, db_file)
111 log.debug('initialing cache-backend=%s db in %s', self.__class__.__name__, db_file)
112 db_file_dir = os.path.dirname(db_file)
113 if not os.path.isdir(db_file_dir):
114 os.makedirs(db_file_dir)
115
104 try:
116 try:
105 super(FileNamespaceBackend, self).__init__(arguments)
117 super(FileNamespaceBackend, self).__init__(arguments)
106 except Exception:
118 except Exception:
@@ -108,7 +120,10 b' class FileNamespaceBackend(PickleSeriali'
108 raise
120 raise
109
121
110 def __repr__(self):
122 def __repr__(self):
111 return '{} `{}`'.format(self.__class__, self.filename)
123 return f'{self.__class__}(file=`{self.filename}`)'
124
125 def __str__(self):
126 return self.__repr__()
112
127
113 def list_keys(self, prefix: bytes = b''):
128 def list_keys(self, prefix: bytes = b''):
114 prefix = b'%b:%b' % (safe_bytes(self.key_prefix), safe_bytes(prefix))
129 prefix = b'%b:%b' % (safe_bytes(self.key_prefix), safe_bytes(prefix))
@@ -136,14 +151,22 b' class BaseRedisBackend(redis_backend.Red'
136 key_prefix = ''
151 key_prefix = ''
137
152
138 def __init__(self, arguments):
153 def __init__(self, arguments):
154 self.db_conn = arguments.get('host', '') or arguments.get('url', '') or 'redis-host'
139 super(BaseRedisBackend, self).__init__(arguments)
155 super(BaseRedisBackend, self).__init__(arguments)
156
140 self._lock_timeout = self.lock_timeout
157 self._lock_timeout = self.lock_timeout
141 self._lock_auto_renewal = asbool(arguments.pop("lock_auto_renewal", True))
158 self._lock_auto_renewal = str2bool(arguments.pop("lock_auto_renewal", True))
142
159
143 if self._lock_auto_renewal and not self._lock_timeout:
160 if self._lock_auto_renewal and not self._lock_timeout:
144 # set default timeout for auto_renewal
161 # set default timeout for auto_renewal
145 self._lock_timeout = 30
162 self._lock_timeout = 30
146
163
164 def __repr__(self):
165 return f'{self.__class__}(conn=`{self.db_conn}`)'
166
167 def __str__(self):
168 return self.__repr__()
169
147 def _create_client(self):
170 def _create_client(self):
148 args = {}
171 args = {}
149
172
@@ -163,7 +186,7 b' class BaseRedisBackend(redis_backend.Red'
163 self.reader_client = self.writer_client
186 self.reader_client = self.writer_client
164
187
165 def list_keys(self, prefix=''):
188 def list_keys(self, prefix=''):
166 prefix = '{}:{}*'.format(self.key_prefix, prefix)
189 prefix = f'{self.key_prefix}:{prefix}*'
167 return self.reader_client.keys(prefix)
190 return self.reader_client.keys(prefix)
168
191
169 def get_store(self):
192 def get_store(self):
@@ -171,7 +194,7 b' class BaseRedisBackend(redis_backend.Red'
171
194
172 def get_mutex(self, key):
195 def get_mutex(self, key):
173 if self.distributed_lock:
196 if self.distributed_lock:
174 lock_key = '_lock_{0}'.format(safe_str(key))
197 lock_key = f'_lock_{safe_str(key)}'
175 return get_mutex_lock(
198 return get_mutex_lock(
176 self.writer_client, lock_key,
199 self.writer_client, lock_key,
177 self._lock_timeout,
200 self._lock_timeout,
@@ -208,10 +231,10 b' def get_mutex_lock(client, lock_key, loc'
208 )
231 )
209
232
210 def __repr__(self):
233 def __repr__(self):
211 return "{}:{}".format(self.__class__.__name__, lock_key)
234 return f"{self.__class__.__name__}:{lock_key}"
212
235
213 def __str__(self):
236 def __str__(self):
214 return "{}:{}".format(self.__class__.__name__, lock_key)
237 return f"{self.__class__.__name__}:{lock_key}"
215
238
216 def __init__(self):
239 def __init__(self):
217 self.lock = self.get_lock()
240 self.lock = self.get_lock()
@@ -18,6 +18,7 b''
18 import functools
18 import functools
19 import logging
19 import logging
20 import os
20 import os
21 import threading
21 import time
22 import time
22
23
23 import decorator
24 import decorator
@@ -25,6 +26,7 b' from dogpile.cache import CacheRegion'
25
26
26 from vcsserver.lib.rc_cache import region_meta
27 from vcsserver.lib.rc_cache import region_meta
27 from vcsserver.str_utils import safe_bytes
28 from vcsserver.str_utils import safe_bytes
29 from vcsserver.type_utils import str2bool
28 from vcsserver.utils import sha1
30 from vcsserver.utils import sha1
29
31
30 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
@@ -32,6 +34,9 b' log = logging.getLogger(__name__)'
32
34
33 class RhodeCodeCacheRegion(CacheRegion):
35 class RhodeCodeCacheRegion(CacheRegion):
34
36
37 def __repr__(self):
38 return f'{self.__class__}(name={self.name})'
39
35 def conditional_cache_on_arguments(
40 def conditional_cache_on_arguments(
36 self, namespace=None,
41 self, namespace=None,
37 expiration_time=None,
42 expiration_time=None,
@@ -144,43 +149,58 b' def backend_key_generator(backend):'
144
149
145
150
146 def key_generator(backend, namespace, fn):
151 def key_generator(backend, namespace, fn):
147 fname = fn.__name__
152 func_name = fn.__name__
148
153
149 def generate_key(*args):
154 def generate_key(*args):
150 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
155 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
151 namespace_pref = namespace or 'default_namespace'
156 namespace_pref = namespace or 'default_namespace'
152 arg_key = compute_key_from_params(*args)
157 arg_key = compute_key_from_params(*args)
153 final_key = f"{backend_prefix}:{namespace_pref}:{fname}_{arg_key}"
158 final_key = f"{backend_prefix}:{namespace_pref}:{func_name}_{arg_key}"
154
159
155 return final_key
160 return final_key
156
161
157 return generate_key
162 return generate_key
158
163
159
164
160 def get_or_create_region(region_name, region_namespace=None):
165 def get_or_create_region(region_name, region_namespace: str = None):
161 from vcsserver.lib.rc_cache.backends import FileNamespaceBackend
166 from vcsserver.lib.rc_cache.backends import FileNamespaceBackend
167
162 region_obj = region_meta.dogpile_cache_regions.get(region_name)
168 region_obj = region_meta.dogpile_cache_regions.get(region_name)
163 if not region_obj:
169 if not region_obj:
164 reg_keys = list(region_meta.dogpile_cache_regions.keys())
170 reg_keys = list(region_meta.dogpile_cache_regions.keys())
165 raise EnvironmentError(f'Region `{region_name}` not in configured: {reg_keys}.')
171 raise EnvironmentError(f'Region `{region_name}` not in configured: {reg_keys}.')
166
172
167 region_uid_name = f'{region_name}:{region_namespace}'
173 region_uid_name = f'{region_name}:{region_namespace}'
174
168 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
175 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
176 if not region_namespace:
177 raise ValueError(f'{FileNamespaceBackend} used requires to specify region_namespace param')
178
169 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
179 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
170 if region_exist:
180 if region_exist:
171 log.debug('Using already configured region: %s', region_namespace)
181 log.debug('Using already configured region: %s', region_namespace)
172 return region_exist
182 return region_exist
173 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
183
174 expiration_time = region_obj.expiration_time
184 expiration_time = region_obj.expiration_time
175
185
176 if not os.path.isdir(cache_dir):
186 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
177 os.makedirs(cache_dir)
187 namespace_cache_dir = cache_dir
188
189 # we default the namespace_cache_dir to our default cache dir.
190 # however if this backend is configured with filename= param, we prioritize that
191 # so all caches within that particular region, even those namespaced end up in the same path
192 if region_obj.actual_backend.filename:
193 namespace_cache_dir = os.path.dirname(region_obj.actual_backend.filename)
194
195 if not os.path.isdir(namespace_cache_dir):
196 os.makedirs(namespace_cache_dir)
178 new_region = make_region(
197 new_region = make_region(
179 name=region_uid_name,
198 name=region_uid_name,
180 function_key_generator=backend_key_generator(region_obj.actual_backend)
199 function_key_generator=backend_key_generator(region_obj.actual_backend)
181 )
200 )
201
182 namespace_filename = os.path.join(
202 namespace_filename = os.path.join(
183 cache_dir, f"{region_namespace}.cache.dbm")
203 namespace_cache_dir, f"{region_name}_{region_namespace}.cache_db")
184 # special type that allows 1db per namespace
204 # special type that allows 1db per namespace
185 new_region.configure(
205 new_region.configure(
186 backend='dogpile.cache.rc.file_namespace',
206 backend='dogpile.cache.rc.file_namespace',
@@ -195,13 +215,18 b' def get_or_create_region(region_name, re'
195 return region_obj
215 return region_obj
196
216
197
217
198 def clear_cache_namespace(cache_region, cache_namespace_uid, invalidate=False):
218 def clear_cache_namespace(cache_region: str | RhodeCodeCacheRegion, cache_namespace_uid: str, invalidate: bool = False, hard: bool = False):
199 region = get_or_create_region(cache_region, cache_namespace_uid)
219 if not isinstance(cache_region, RhodeCodeCacheRegion):
200 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
220 cache_region = get_or_create_region(cache_region, cache_namespace_uid)
221
222 cache_keys = cache_region.backend.list_keys(prefix=cache_namespace_uid)
201 num_delete_keys = len(cache_keys)
223 num_delete_keys = len(cache_keys)
202 if invalidate:
224 if invalidate:
203 region.invalidate(hard=False)
225 # NOTE: The CacheRegion.invalidate() method’s default mode of
226 # operation is to set a timestamp local to this CacheRegion in this Python process only.
227 # It does not impact other Python processes or regions as the timestamp is only stored locally in memory.
228 cache_region.invalidate(hard=hard)
204 else:
229 else:
205 if num_delete_keys:
230 if num_delete_keys:
206 region.delete_multi(cache_keys)
231 cache_region.delete_multi(cache_keys)
207 return num_delete_keys
232 return num_delete_keys
General Comments 0
You need to be logged in to leave comments. Login now