##// END OF EJS Templates
fix(permission-flush): use delete method for permission cache invalidation as it's multi-process safe....
super-admin -
r5266:a1331d35 default
parent child Browse files
Show More
@@ -145,7 +145,7 b' dev-srv:'
145 145 .PHONY: dev-srv-g
146 146 ## run gunicorn multi process workers
147 147 dev-srv-g:
148 gunicorn --workers=2 --paste .dev/dev.ini --bind=0.0.0.0:10020 --config=.dev/gunicorn_config.py
148 gunicorn --paste .dev/dev.ini --bind=0.0.0.0:10020 --config=.dev/gunicorn_config.py --timeout=120
149 149
150 150
151 151 # Default command on calling make
@@ -44,7 +44,7 b' def trigger_user_permission_flush(event)'
44 44 for user_id in affected_user_ids:
45 45 for cache_namespace_uid_tmpl in cache_namespaces:
46 46 cache_namespace_uid = cache_namespace_uid_tmpl.format(user_id)
47 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid, method=rc_cache.CLEAR_INVALIDATE)
47 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid, method=rc_cache.CLEAR_DELETE)
48 48 log.debug('Invalidated %s cache keys for user_id: %s and namespace %s',
49 49 del_keys, user_id, cache_namespace_uid)
50 50
@@ -1313,7 +1313,7 b' class UsersView(UserAppView):'
1313 1313 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1314 1314
1315 1315 cache_namespace_uid = f'cache_user_auth.{rc_cache.PERMISSIONS_CACHE_VER}.{self.db_user.user_id}'
1316 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1316 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid, method=rc_cache.CLEAR_DELETE)
1317 1317
1318 1318 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1319 1319
@@ -74,10 +74,20 b' class LRUMemoryBackend(memory_backend.Me'
74 74 # we don't care if key isn't there at deletion
75 75 pass
76 76
77 def list_keys(self, prefix):
78 return list(self._cache.keys())
79
77 80 def delete_multi(self, keys):
78 81 for key in keys:
79 82 self.delete(key)
80 83
84 def delete_multi_by_prefix(self, prefix):
85 cache_keys = self.list_keys(prefix=prefix)
86 num_affected_keys = len(cache_keys)
87 if num_affected_keys:
88 self.delete_multi(cache_keys)
89 return num_affected_keys
90
81 91
82 92 class PickleSerializer:
83 93 serializer: None | Serializer = staticmethod( # type: ignore
@@ -178,6 +188,13 b' class FileNamespaceBackend(PickleSeriali'
178 188 log.error('Failed to fetch DBM keys from DB: %s', self.get_store())
179 189 raise
180 190
191 def delete_multi_by_prefix(self, prefix):
192 cache_keys = self.list_keys(prefix=prefix)
193 num_affected_keys = len(cache_keys)
194 if num_affected_keys:
195 self.delete_multi(cache_keys)
196 return num_affected_keys
197
181 198 def get_store(self):
182 199 return self.filename
183 200
@@ -227,6 +244,25 b' class BaseRedisBackend(redis_backend.Red'
227 244 prefix = self._get_keys_pattern(prefix)
228 245 return self.reader_client.keys(prefix)
229 246
247 def delete_multi_by_prefix(self, prefix, use_lua=False):
248 if use_lua:
249 # high efficient LUA script to delete ALL keys by prefix...
250 lua = """local keys = redis.call('keys', ARGV[1])
251 for i=1,#keys,5000 do
252 redis.call('del', unpack(keys, i, math.min(i+(5000-1), #keys)))
253 end
254 return #keys"""
255 num_affected_keys = self.writer_client.eval(
256 lua,
257 0,
258 f"{prefix}*")
259 else:
260 cache_keys = self.list_keys(prefix=prefix)
261 num_affected_keys = len(cache_keys)
262 if num_affected_keys:
263 self.delete_multi(cache_keys)
264 return num_affected_keys
265
230 266 def get_store(self):
231 267 return self.reader_client.connection_pool
232 268
@@ -232,14 +232,15 b' def get_or_create_region(region_name, re'
232 232 return region_obj
233 233
234 234
235 def clear_cache_namespace(cache_region: str | RhodeCodeCacheRegion, cache_namespace_uid: str, method: str):
235 def clear_cache_namespace(cache_region: str | RhodeCodeCacheRegion, cache_namespace_uid: str, method: str) -> int:
236 236 from . import CLEAR_DELETE, CLEAR_INVALIDATE
237 237
238 238 if not isinstance(cache_region, RhodeCodeCacheRegion):
239 239 cache_region = get_or_create_region(cache_region, cache_namespace_uid)
240 log.debug('clearing cache region: %s with method=%s', cache_region, method)
240 log.debug('clearing cache region: %s [prefix:%s] with method=%s',
241 cache_region, cache_namespace_uid, method)
241 242
242 num_affected_keys = None
243 num_affected_keys = 0
243 244
244 245 if method == CLEAR_INVALIDATE:
245 246 # NOTE: The CacheRegion.invalidate() method’s default mode of
@@ -248,10 +249,7 b' def clear_cache_namespace(cache_region: '
248 249 cache_region.invalidate(hard=True)
249 250
250 251 if method == CLEAR_DELETE:
251 cache_keys = cache_region.backend.list_keys(prefix=cache_namespace_uid)
252 num_affected_keys = len(cache_keys)
253 if num_affected_keys:
254 cache_region.delete_multi(cache_keys)
252 num_affected_keys = cache_region.backend.delete_multi_by_prefix(prefix=cache_namespace_uid)
255 253
256 254 return num_affected_keys
257 255
General Comments 0
You need to be logged in to leave comments. Login now