##// END OF EJS Templates
caches: use delete_multi for clearing out cache keys....
marcink -
r2888:fc338c66 default
parent child Browse files
Show More
@@ -1,107 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2018 RhodeCode GmbH
3 # Copyright (C) 2015-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import os
20 import os
21 import logging
21 import logging
22 from dogpile.cache import make_region
22 from dogpile.cache import make_region
23
23
24 from rhodecode.lib.utils import safe_str, sha1
24 from rhodecode.lib.utils import safe_str, sha1
25 from . import region_meta
25 from . import region_meta
26
26
27 log = logging.getLogger(__name__)
27 log = logging.getLogger(__name__)
28
28
29
29
30 def get_default_cache_settings(settings, prefixes=None):
30 def get_default_cache_settings(settings, prefixes=None):
31 prefixes = prefixes or []
31 prefixes = prefixes or []
32 cache_settings = {}
32 cache_settings = {}
33 for key in settings.keys():
33 for key in settings.keys():
34 for prefix in prefixes:
34 for prefix in prefixes:
35 if key.startswith(prefix):
35 if key.startswith(prefix):
36 name = key.split(prefix)[1].strip()
36 name = key.split(prefix)[1].strip()
37 val = settings[key]
37 val = settings[key]
38 if isinstance(val, basestring):
38 if isinstance(val, basestring):
39 val = val.strip()
39 val = val.strip()
40 cache_settings[name] = val
40 cache_settings[name] = val
41 return cache_settings
41 return cache_settings
42
42
43
43
44 def compute_key_from_params(*args):
44 def compute_key_from_params(*args):
45 """
45 """
46 Helper to compute key from given params to be used in cache manager
46 Helper to compute key from given params to be used in cache manager
47 """
47 """
48 return sha1("_".join(map(safe_str, args)))
48 return sha1("_".join(map(safe_str, args)))
49
49
50
50
51 def key_generator(namespace, fn):
51 def key_generator(namespace, fn):
52 fname = fn.__name__
52 fname = fn.__name__
53
53
54 def generate_key(*args):
54 def generate_key(*args):
55 namespace_pref = namespace or 'default'
55 namespace_pref = namespace or 'default'
56 arg_key = compute_key_from_params(*args)
56 arg_key = compute_key_from_params(*args)
57 final_key = "{}:{}_{}".format(namespace_pref, fname, arg_key)
57 final_key = "{}:{}_{}".format(namespace_pref, fname, arg_key)
58
58
59 return final_key
59 return final_key
60
60
61 return generate_key
61 return generate_key
62
62
63
63
64 def get_or_create_region(region_name, region_namespace=None):
64 def get_or_create_region(region_name, region_namespace=None):
65 from rhodecode.lib.rc_cache.backends import FileNamespaceBackend
65 from rhodecode.lib.rc_cache.backends import FileNamespaceBackend
66 region_obj = region_meta.dogpile_cache_regions.get(region_name)
66 region_obj = region_meta.dogpile_cache_regions.get(region_name)
67 if not region_obj:
67 if not region_obj:
68 raise EnvironmentError(
68 raise EnvironmentError(
69 'Region `{}` not in configured: {}.'.format(
69 'Region `{}` not in configured: {}.'.format(
70 region_name, region_meta.dogpile_cache_regions.keys()))
70 region_name, region_meta.dogpile_cache_regions.keys()))
71
71
72 region_uid_name = '{}:{}'.format(region_name, region_namespace)
72 region_uid_name = '{}:{}'.format(region_name, region_namespace)
73 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
73 if isinstance(region_obj.actual_backend, FileNamespaceBackend):
74 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
74 region_exist = region_meta.dogpile_cache_regions.get(region_namespace)
75 if region_exist:
75 if region_exist:
76 log.debug('Using already configured region: %s', region_namespace)
76 log.debug('Using already configured region: %s', region_namespace)
77 return region_exist
77 return region_exist
78 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
78 cache_dir = region_meta.dogpile_config_defaults['cache_dir']
79 expiration_time = region_obj.expiration_time
79 expiration_time = region_obj.expiration_time
80
80
81 if not os.path.isdir(cache_dir):
81 if not os.path.isdir(cache_dir):
82 os.makedirs(cache_dir)
82 os.makedirs(cache_dir)
83 new_region = make_region(
83 new_region = make_region(
84 name=region_uid_name, function_key_generator=key_generator
84 name=region_uid_name, function_key_generator=key_generator
85 )
85 )
86 namespace_filename = os.path.join(
86 namespace_filename = os.path.join(
87 cache_dir, "{}.cache.dbm".format(region_namespace))
87 cache_dir, "{}.cache.dbm".format(region_namespace))
88 # special type that allows 1db per namespace
88 # special type that allows 1db per namespace
89 new_region.configure(
89 new_region.configure(
90 backend='dogpile.cache.rc.file_namespace',
90 backend='dogpile.cache.rc.file_namespace',
91 expiration_time=expiration_time,
91 expiration_time=expiration_time,
92 arguments={"filename": namespace_filename}
92 arguments={"filename": namespace_filename}
93 )
93 )
94
94
95 # create and save in region caches
95 # create and save in region caches
96 log.debug('configuring new region: %s',region_uid_name)
96 log.debug('configuring new region: %s',region_uid_name)
97 region_obj = region_meta.dogpile_cache_regions[region_namespace] = new_region
97 region_obj = region_meta.dogpile_cache_regions[region_namespace] = new_region
98
98
99 return region_obj
99 return region_obj
100
100
101
101
102 def clear_cache_namespace(cache_region, cache_namespace_uid):
102 def clear_cache_namespace(cache_region, cache_namespace_uid):
103 region = get_or_create_region(cache_region, cache_namespace_uid)
103 region = get_or_create_region(cache_region, cache_namespace_uid)
104 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
104 cache_keys = region.backend.list_keys(prefix=cache_namespace_uid)
105 for k in cache_keys:
105 region.delete_multi(cache_keys)
106 region.delete(k)
107 return len(cache_keys)
106 return len(cache_keys)
@@ -1,109 +1,108 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22
22
23 import pytest
23 import pytest
24
24
25 from rhodecode.lib import rc_cache
25 from rhodecode.lib import rc_cache
26
26
27
27
28 @pytest.mark.usefixtures( 'app')
28 @pytest.mark.usefixtures( 'app')
29 class TestCaches(object):
29 class TestCaches(object):
30
30
31 def test_cache_decorator_init_not_configured(self):
31 def test_cache_decorator_init_not_configured(self):
32 with pytest.raises(EnvironmentError):
32 with pytest.raises(EnvironmentError):
33 rc_cache.get_or_create_region('dontexist')
33 rc_cache.get_or_create_region('dontexist')
34
34
35 @pytest.mark.parametrize('region_name', [
35 @pytest.mark.parametrize('region_name', [
36 'cache_perms', u'cache_perms',
36 'cache_perms', u'cache_perms',
37 ])
37 ])
38 def test_cache_decorator_init(self, region_name):
38 def test_cache_decorator_init(self, region_name):
39 namespace = region_name
39 namespace = region_name
40 cache_region = rc_cache.get_or_create_region(
40 cache_region = rc_cache.get_or_create_region(
41 region_name, region_namespace=namespace)
41 region_name, region_namespace=namespace)
42 assert cache_region
42 assert cache_region
43
43
44 @pytest.mark.parametrize('example_input', [
44 @pytest.mark.parametrize('example_input', [
45 ('',),
45 ('',),
46 (u'/ac',),
46 (u'/ac',),
47 (u'/ac', 1, 2, object()),
47 (u'/ac', 1, 2, object()),
48 (u'/Δ™Δ‡c', 1, 2, object()),
48 (u'/Δ™Δ‡c', 1, 2, object()),
49 ('/Δ…ac',),
49 ('/Δ…ac',),
50 (u'/ac', ),
50 (u'/ac', ),
51 ])
51 ])
52 def test_cache_manager_create_key(self, example_input):
52 def test_cache_manager_create_key(self, example_input):
53 key = rc_cache.utils.compute_key_from_params(*example_input)
53 key = rc_cache.utils.compute_key_from_params(*example_input)
54 assert key
54 assert key
55
55
56 @pytest.mark.parametrize('example_namespace', [
56 @pytest.mark.parametrize('example_namespace', [
57 'namespace', None
57 'namespace', None
58 ])
58 ])
59 @pytest.mark.parametrize('example_input', [
59 @pytest.mark.parametrize('example_input', [
60 ('',),
60 ('',),
61 (u'/ac',),
61 (u'/ac',),
62 (u'/ac', 1, 2, object()),
62 (u'/ac', 1, 2, object()),
63 (u'/Δ™Δ‡c', 1, 2, object()),
63 (u'/Δ™Δ‡c', 1, 2, object()),
64 ('/Δ…ac',),
64 ('/Δ…ac',),
65 (u'/ac', ),
65 (u'/ac', ),
66 ])
66 ])
67 def test_cache_keygen(self, example_input, example_namespace):
67 def test_cache_keygen(self, example_input, example_namespace):
68 def func_wrapped():
68 def func_wrapped():
69 return 1
69 return 1
70 func = rc_cache.utils.key_generator(example_namespace, func_wrapped)
70 func = rc_cache.utils.key_generator(example_namespace, func_wrapped)
71 key = func(*example_input)
71 key = func(*example_input)
72 assert key
72 assert key
73
73
74 def test_store_value_in_cache(self):
74 def test_store_value_in_cache(self):
75 cache_region = rc_cache.get_or_create_region('cache_perms')
75 cache_region = rc_cache.get_or_create_region('cache_perms')
76 # make sure we empty the cache now
76 # make sure we empty the cache now
77 for key in cache_region.backend.list_keys():
77 cache_region.delete_multi(cache_region.backend.list_keys())
78 cache_region.delete(key)
79
78
80 assert cache_region.backend.list_keys() == []
79 assert cache_region.backend.list_keys() == []
81
80
82 @cache_region.cache_on_arguments(expiration_time=5)
81 @cache_region.cache_on_arguments(expiration_time=5)
83 def compute(key):
82 def compute(key):
84 return time.time()
83 return time.time()
85
84
86 for x in range(10):
85 for x in range(10):
87 compute(x)
86 compute(x)
88
87
89 assert len(set(cache_region.backend.list_keys())) == 10
88 assert len(set(cache_region.backend.list_keys())) == 10
90
89
91 def test_store_and_get_value_from_region(self):
90 def test_store_and_get_value_from_region(self):
92 cache_region = rc_cache.get_or_create_region('cache_perms')
91 cache_region = rc_cache.get_or_create_region('cache_perms')
93 # make sure we empty the cache now
92 # make sure we empty the cache now
94 for key in cache_region.backend.list_keys():
93 for key in cache_region.backend.list_keys():
95 cache_region.delete(key)
94 cache_region.delete(key)
96 assert cache_region.backend.list_keys() == []
95 assert cache_region.backend.list_keys() == []
97
96
98 @cache_region.cache_on_arguments(expiration_time=5)
97 @cache_region.cache_on_arguments(expiration_time=5)
99 def compute(key):
98 def compute(key):
100 return time.time()
99 return time.time()
101
100
102 result = set()
101 result = set()
103 for x in range(10):
102 for x in range(10):
104 ret = compute('x')
103 ret = compute('x')
105 result.add(ret)
104 result.add(ret)
106
105
107 # once computed we have only one value (the same from cache)
106 # once computed we have only one value (the same from cache)
108 # after executing it 10x
107 # after executing it 10x
109 assert len(result) == 1
108 assert len(result) == 1
General Comments 0
You need to be logged in to leave comments. Login now