summary refs log tree commit diff
path: root/synapse/config/cache.py
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/config/cache.py')
-rw-r--r--synapse/config/cache.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/synapse/config/cache.py b/synapse/config/cache.py
new file mode 100644

index 0000000000..bb68551954 --- /dev/null +++ b/synapse/config/cache.py
@@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Dict + +from ._base import Config, ConfigError + +_CACHES = {} +_CACHE_PREFIX = "SYNAPSE_CACHE_FACTOR" +DEFAULT_CACHE_SIZE_FACTOR = float(os.environ.get(_CACHE_PREFIX, 0.5)) + +_DEFAULT_CONFIG = """\ +# Cache configuration +# +# 'global_factor' controls the global cache factor. This overrides the +# "SYNAPSE_CACHE_FACTOR" environment variable. +# +# 'per_cache_factors' is a dictionary of cache name to cache factor for that +# individual cache. +# +#caches: +# global_factor: 0.5 +# per_cache_factors: +# get_users_who_share_room_with_user: 2 +# +""" + +# 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 + 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", {}) + + self.global_factor = cache_config.get( + "global_factor", DEFAULT_CACHE_SIZE_FACTOR + ) + if not isinstance(self.global_factor, (int, float)): + raise ConfigError("caches.global_factor must be a number.") + + # Set the global one so that it's reflected in new caches + DEFAULT_CACHE_SIZE_FACTOR = self.global_factor + + # Load cache factors from the environment, but override them with the + # ones in the config file if they exist + individual_factors = { + key[len(_CACHE_PREFIX) + 1 :].lower(): float(val) + for key, val in self._environ.items() + if key.startswith(_CACHE_PREFIX + "_") + } + + individual_factors_config = cache_config.get("per_cache_factors", {}) or {} + if not isinstance(individual_factors_config, dict): + raise ConfigError("caches.per_cache_factors must be a dictionary") + + individual_factors.update(individual_factors_config) + + self.cache_factors = dict() # type: Dict[str, float] + + for cache, factor in individual_factors.items(): + if not isinstance(factor, (int, float)): + raise ConfigError( + "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()