summary refs log tree commit diff
diff options
context:
space:
mode:
authorAmber H. Brown <hawkowl@atleastfornow.net>2020-02-17 17:47:56 +1100
committerAmber H. Brown <hawkowl@atleastfornow.net>2020-02-17 17:47:56 +1100
commit5f508e728af3f842f78ec435fd0393dd3a07e854 (patch)
treeeecf6826891b422b7ba21fcc00055f175b7a3866
parentMerge remote-tracking branch 'origin/develop' into hawkowl/cache-config-witho... (diff)
downloadsynapse-5f508e728af3f842f78ec435fd0393dd3a07e854.tar.xz
add tests for individual cache sizing, and fix up the individual cache sizing logic that got deleted when resizing-on-the-fly did
-rw-r--r--synapse/config/cache.py30
-rw-r--r--tests/config/test_cache.py66
2 files changed, 91 insertions, 5 deletions
diff --git a/synapse/config/cache.py b/synapse/config/cache.py
index d6dadc5fde..dbc2971eeb 100644
--- a/synapse/config/cache.py
+++ b/synapse/config/cache.py
@@ -39,20 +39,35 @@ _DEFAULT_CONFIG = """\
 #
 """
 
+# Callback to ensure that all caches are the correct size, registered when the
+# configuration has been loaded.
+_ENSURE_CORRECT_CACHE_SIZING = None
+
 
 def add_resizable_cache(cache_name, cache_resize_callback):
     _CACHES[cache_name.lower()] = cache_resize_callback
-    cache_resize_callback(DEFAULT_CACHE_SIZE_FACTOR)
+    if _ENSURE_CORRECT_CACHE_SIZING:
+        _ENSURE_CORRECT_CACHE_SIZING()
 
 
 class CacheConfig(Config):
     section = "caches"
     _environ = os.environ
 
+    @staticmethod
+    def _reset():
+        global DEFAULT_CACHE_SIZE_FACTOR
+        global _ENSURE_CORRECT_CACHE_SIZING
+
+        DEFAULT_CACHE_SIZE_FACTOR = float(os.environ.get(_CACHE_PREFIX, 0.5))
+        _ENSURE_CORRECT_CACHE_SIZING = None
+        _CACHES.clear()
+
     def read_config(self, config, **kwargs):
         self.event_cache_size = self.parse_size(config.get("event_cache_size", "10K"))
 
         global DEFAULT_CACHE_SIZE_FACTOR
+        global _ENSURE_CORRECT_CACHE_SIZING
 
         cache_config = config.get("caches", {})
 
@@ -79,9 +94,7 @@ class CacheConfig(Config):
 
         individual_factors.update(individual_factors_config)
 
-        self.cache_factors = defaultdict(
-            lambda: self.global_factor
-        )  # type: DefaultDict[str, float]
+        self.cache_factors = dict()  # type: Dict[str, float]
 
         for cache, factor in individual_factors.items():
             if not isinstance(factor, (int, float)):
@@ -89,3 +102,12 @@ class CacheConfig(Config):
                     "caches.per_cache_factors.%s must be a number" % (cache.lower(),)
                 )
             self.cache_factors[cache.lower()] = factor
+
+        # Register the global callback so that the individual cache sizes get set.
+        def ensure_cache_sizes():
+            for cache_name, callback in _CACHES.items():
+                new_factor = self.cache_factors.get(cache_name, self.global_factor)
+                callback(new_factor)
+
+        _ENSURE_CORRECT_CACHE_SIZING = ensure_cache_sizes
+        _ENSURE_CORRECT_CACHE_SIZING()
diff --git a/tests/config/test_cache.py b/tests/config/test_cache.py
index 94370265f1..f8901a4612 100644
--- a/tests/config/test_cache.py
+++ b/tests/config/test_cache.py
@@ -14,7 +14,8 @@
 # limitations under the License.
 
 from synapse.config._base import Config, RootConfig
-from synapse.config.cache import CacheConfig
+from synapse.config.cache import CacheConfig, add_resizable_cache
+from synapse.util.caches.lrucache import LruCache
 
 from tests.unittest import TestCase
 
@@ -28,6 +29,9 @@ class TestConfig(RootConfig):
 
 
 class CacheConfigTests(TestCase):
+    def setUp(self):
+        CacheConfig._reset()
+
     def test_individual_caches_from_environ(self):
         """
         Individual cache factors will be loaded from the environment.
@@ -59,3 +63,63 @@ class CacheConfigTests(TestCase):
             dict(t.caches.cache_factors),
             {"foo": 2.0, "bar": 3.0, "something_or_other": 2.0},
         )
+
+    def test_individual_instantiated_before_config_load(self):
+        """
+        If a cache is instantiated before the config is read, it will be given
+        the default cache size in the interim, and then resized once the config
+        is loaded.
+        """
+        cache = LruCache(100)
+        add_resizable_cache("foo", cache.set_cache_factor)
+        self.assertEqual(cache.max_size, 50)
+
+        config = {"caches": {"per_cache_factors": {"foo": 3}}}
+        t = TestConfig()
+        t.read_config(config, config_dir_path="", data_dir_path="")
+
+        self.assertEqual(cache.max_size, 300)
+
+    def test_individual_instantiated_after_config_load(self):
+        """
+        If a cache is instantiated after the config is read, it will be
+        immediately resized to the correct size given the per_cache_factor if
+        there is one.
+        """
+        config = {"caches": {"per_cache_factors": {"foo": 2}}}
+        t = TestConfig()
+        t.read_config(config, config_dir_path="", data_dir_path="")
+
+        cache = LruCache(100)
+        add_resizable_cache("foo", cache.set_cache_factor)
+        self.assertEqual(cache.max_size, 200)
+
+    def test_global_instantiated_before_config_load(self):
+        """
+        If a cache is instantiated before the config is read, it will be given
+        the default cache size in the interim, and then resized to the new
+        default cache size once the config is loaded.
+        """
+        cache = LruCache(100)
+        add_resizable_cache("foo", cache.set_cache_factor)
+        self.assertEqual(cache.max_size, 50)
+
+        config = {"caches": {"global_factor": 4}}
+        t = TestConfig()
+        t.read_config(config, config_dir_path="", data_dir_path="")
+
+        self.assertEqual(cache.max_size, 400)
+
+    def test_global_instantiated_after_config_load(self):
+        """
+        If a cache is instantiated after the config is read, it will be
+        immediately resized to the correct size given the global factor if there
+        is no per-cache factor.
+        """
+        config = {"caches": {"global_factor": 1.5}}
+        t = TestConfig()
+        t.read_config(config, config_dir_path="", data_dir_path="")
+
+        cache = LruCache(100)
+        add_resizable_cache("foo", cache.set_cache_factor)
+        self.assertEqual(cache.max_size, 150)