##// 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 3 # RhodeCode VCSServer provides access to different vcs backends via network.
2 4 # Copyright (C) 2014-2020 RhodeCode GmbH
3 5 #
@@ -16,9 +18,20 b''
16 18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 19
18 20 import logging
21 import threading
19 22
20 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 35 module_name = 'vcsserver'
23 36
24 37 register_backend(
@@ -40,14 +53,18 b' register_backend('
40 53
41 54 log = logging.getLogger(__name__)
42 55
43 from . import region_meta
44 from .utils import (
45 backend_key_generator,
46 clear_cache_namespace,
47 get_default_cache_settings,
48 get_or_create_region,
49 make_region,
50 )
56
57 def async_creation_runner(cache, somekey, creator, mutex):
58
59 def runner():
60 try:
61 value = creator()
62 cache.set(somekey, value)
63 finally:
64 mutex.release()
65
66 thread = threading.Thread(target=runner)
67 thread.start()
51 68
52 69
53 70 def configure_dogpile_cache(settings):
@@ -69,13 +86,20 b' def configure_dogpile_cache(settings):'
69 86
70 87 new_region = make_region(
71 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 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 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 103 region_invalidator=new_region.region_invalidator.__class__)
80 104 log.debug('dogpile: registering a new region `%s` %s', namespace_name, region_args)
81 105
@@ -19,9 +19,11 b' import errno'
19 19 import fcntl
20 20 import functools
21 21 import logging
22 import os
22 23 import pickle
23 import time
24 #import time
24 25
26 #import gevent
25 27 import msgpack
26 28 import redis
27 29
@@ -34,10 +36,10 b' from dogpile.cache.backends import memor'
34 36 from dogpile.cache.backends import redis as redis_backend
35 37 from dogpile.cache.backends.file import FileLock
36 38 from dogpile.cache.util import memoized_property
37 from pyramid.settings import asbool
38 39
39 40 from vcsserver.lib.memory_lru_dict import LRUDict, LRUDictDebug
40 41 from vcsserver.str_utils import safe_bytes, safe_str
42 from vcsserver.type_utils import str2bool
41 43
42 44 _default_max_size = 1024
43 45
@@ -49,15 +51,21 b' class LRUMemoryBackend(memory_backend.Me'
49 51 pickle_values = False
50 52
51 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 56 LRUDictClass = LRUDict
55 57 if arguments.pop('log_key_count', None):
56 58 LRUDictClass = LRUDictDebug
57 59
58 arguments['cache_dict'] = LRUDictClass(max_size)
60 arguments['cache_dict'] = LRUDictClass(self.max_size)
59 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 69 def delete(self, key):
62 70 try:
63 71 del self._cache[key]
@@ -100,7 +108,11 b' class FileNamespaceBackend(PickleSeriali'
100 108 arguments['lock_factory'] = CustomLockFactory
101 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 116 try:
105 117 super(FileNamespaceBackend, self).__init__(arguments)
106 118 except Exception:
@@ -108,7 +120,10 b' class FileNamespaceBackend(PickleSeriali'
108 120 raise
109 121
110 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 128 def list_keys(self, prefix: bytes = b''):
114 129 prefix = b'%b:%b' % (safe_bytes(self.key_prefix), safe_bytes(prefix))
@@ -136,14 +151,22 b' class BaseRedisBackend(redis_backend.Red'
136 151 key_prefix = ''
137 152
138 153 def __init__(self, arguments):
154 self.db_conn = arguments.get('host', '') or arguments.get('url', '') or 'redis-host'
139 155 super(BaseRedisBackend, self).__init__(arguments)
156
140 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 160 if self._lock_auto_renewal and not self._lock_timeout:
144 161 # set default timeout for auto_renewal
145 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 170 def _create_client(self):
148 171 args = {}
149 172
@@ -163,7 +186,7 b' class BaseRedisBackend(redis_backend.Red'
163 186 self.reader_client = self.writer_client
164 187
165 188 def list_keys(self, prefix=''):
166 prefix = '{}:{}*'.format(self.key_prefix, prefix)
189 prefix = f'{self.key_prefix}:{prefix}*'
167 190 return self.reader_client.keys(prefix)
168 191
169 192 def get_store(self):
@@ -171,7 +194,7 b' class BaseRedisBackend(redis_backend.Red'
171 194
172 195 def get_mutex(self, key):
173 196 if self.distributed_lock:
174 lock_key = '_lock_{0}'.format(safe_str(key))
197 lock_key = f'_lock_{safe_str(key)}'
175 198 return get_mutex_lock(
176 199 self.writer_client, lock_key,
177 200 self._lock_timeout,
@@ -208,10 +231,10 b' def get_mutex_lock(client, lock_key, loc'
208 231 )
209 232
210 233 def __repr__(self):
211 return "{}:{}".format(self.__class__.__name__, lock_key)
234 return f"{self.__class__.__name__}:{lock_key}"
212 235
213 236 def __str__(self):
214 return "{}:{}".format(self.__class__.__name__, lock_key)
237 return f"{self.__class__.__name__}:{lock_key}"
215 238
216 239 def __init__(self):
217 240 self.lock = self.get_lock()
@@ -18,6 +18,7 b''
18 18 import functools
19 19 import logging
20 20 import os
21 import threading
21 22 import time
22 23
23 24 import decorator
@@ -25,6 +26,7 b' from dogpile.cache import CacheRegion'
25 26
26 27 from vcsserver.lib.rc_cache import region_meta
27 28 from vcsserver.str_utils import safe_bytes
29 from vcsserver.type_utils import str2bool
28 30 from vcsserver.utils import sha1
29 31
30 32 log = logging.getLogger(__name__)
@@ -32,6 +34,9 b' log = logging.getLogger(__name__)'
32 34
33 35 class RhodeCodeCacheRegion(CacheRegion):
34 36
37 def __repr__(self):
38 return f'{self.__class__}(name={self.name})'
39
35 40 def conditional_cache_on_arguments(
36 41 self, namespace=None,
37 42 expiration_time=None,
@@ -144,43 +149,58 b' def backend_key_generator(backend):'
144 149
145 150
146 151 def key_generator(backend, namespace, fn):
147 fname = fn.__name__
152 func_name = fn.__name__
148 153
149 154 def generate_key(*args):
150 155 backend_prefix = getattr(backend, 'key_prefix', None) or 'backend_prefix'
151 156 namespace_pref = namespace or 'default_namespace'
152 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 160 return final_key
156 161
157 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 166 from vcsserver.lib.rc_cache.backends import FileNamespaceBackend
167
162 168 region_obj = region_meta.dogpile_cache_regions.get(region_name)
163 169 if not region_obj:
164 170 reg_keys = list(region_meta.dogpile_cache_regions.keys())
165 171 raise EnvironmentError(f'Region `{region_name}` not in configured: {reg_keys}.')
166 172
167 173 region_uid_name = f'{region_name}:{region_namespace}'
174
168 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 179 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
170 180 if region_exist:
171 181 log.debug('Using already configured region: %s', region_namespace)
172 182 return region_exist
173 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
183
174 184 expiration_time = region_obj.expiration_time
175 185
176 if not os.path.isdir(cache_dir):
177 os.makedirs(cache_dir)
186 cache_dir = region_meta.dogpile_config_defaults['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 197 new_region = make_region(
179 198 name=region_uid_name,
180 199 function_key_generator=backend_key_generator(region_obj.actual_backend)
181 200 )
201
182 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 204 # special type that allows 1db per namespace
185 205 new_region.configure(
186 206 backend='dogpile.cache.rc.file_namespace',
@@ -195,13 +215,18 b' def get_or_create_region(region_name, re'
195 215 return region_obj
196 216
197 217
198 def clear_cache_namespace(cache_region, cache_namespace_uid, invalidate=False):
199 region = get_or_create_region(cache_region, cache_namespace_uid)
200 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
218 def clear_cache_namespace(cache_region: str | RhodeCodeCacheRegion, cache_namespace_uid: str, invalidate: bool = False, hard: bool = False):
219 if not isinstance(cache_region, RhodeCodeCacheRegion):
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 223 num_delete_keys = len(cache_keys)
202 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 229 else:
205 230 if num_delete_keys:
206 region.delete_multi(cache_keys)
231 cache_region.delete_multi(cache_keys)
207 232 return num_delete_keys
General Comments 0
You need to be logged in to leave comments. Login now