Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Fix specifying cache factors via env vars with * in name. #7580

Merged
merged 4 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/7580.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix specifying individual cache factors for caches with special characters in their name. Regression in v1.14.0rc1.
6 changes: 6 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,12 @@ caches:
# takes priority over setting through the config file.
# Ex. SYNAPSE_CACHE_FACTOR_GET_USERS_WHO_SHARE_ROOM_WITH_USER=2.0
#
# Some caches have '*' and other characters that are not
# alphanumeric or underscores. These caches can be named with or
# without the special characters stripped. For example, to specify
# the cache factor for `*stateGroupCache*` via an environment
# variable would be `SYNAPSE_CACHE_FACTOR_STATEGROUPCACHE=2`.
#
per_cache_factors:
#get_users_who_share_room_with_user: 2.0

Expand Down
44 changes: 39 additions & 5 deletions synapse/config/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@
# limitations under the License.

import os
import re
from typing import Callable, Dict

from ._base import Config, ConfigError

# The prefix for all cache factor-related environment variables
_CACHES = {}
_CACHE_PREFIX = "SYNAPSE_CACHE_FACTOR"

# Map from canonicalised cache name to cache.
_CACHES = {}

_DEFAULT_FACTOR_SIZE = 0.5
_DEFAULT_EVENT_CACHE_SIZE = "10K"

Expand All @@ -37,6 +41,20 @@ def __init__(self):
properties = CacheProperties()


def _canonicalise_cache_name(cache_name: str) -> str:
"""Gets the canonical form of the cache name.

Since we specify cache names in config and environment variables we need to
ignore case and special characters. For example, some caches have asterisks
in their name to donate that they're not attached to a particular database
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in their name to donate that they're not attached to a particular database
in their name to denote that they're not attached to a particular database

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, didn't see this before merge. I fixed it directly.

function, and these asterisks need to be stripped out
"""

cache_name = re.sub(r"[^A-Za-z_1-9]", "", cache_name)

return cache_name.lower()


def add_resizable_cache(cache_name: str, cache_resize_callback: Callable):
"""Register a cache that's size can dynamically change

Expand All @@ -45,7 +63,10 @@ def add_resizable_cache(cache_name: str, cache_resize_callback: Callable):
cache_resize_callback: A callback function that will be ran whenever
the cache needs to be resized
"""
_CACHES[cache_name.lower()] = cache_resize_callback
# Some caches have '*' in them which we strip out.
cache_name = _canonicalise_cache_name(cache_name)

_CACHES[cache_name] = cache_resize_callback

# Ensure all loaded caches are sized appropriately
#
Expand Down Expand Up @@ -105,6 +126,12 @@ def generate_config_section(self, **kwargs):
# takes priority over setting through the config file.
# Ex. SYNAPSE_CACHE_FACTOR_GET_USERS_WHO_SHARE_ROOM_WITH_USER=2.0
#
# Some caches have '*' and other characters that are not
# alphanumeric or underscores. These caches can be named with or
# without the special characters stripped. For example, to specify
# the cache factor for `*stateGroupCache*` via an environment
# variable would be `SYNAPSE_CACHE_FACTOR_STATEGROUPCACHE=2`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may put a 2.0 here instead to keep it in line with the other example, but up to you.

#
per_cache_factors:
#get_users_who_share_room_with_user: 2.0
"""
Expand All @@ -130,10 +157,17 @@ def read_config(self, config, **kwargs):
if not isinstance(individual_factors, dict):
raise ConfigError("caches.per_cache_factors must be a dictionary")

# Canonicalise the cache names *before* updating with the environment
# variables.
individual_factors = {
_canonicalise_cache_name(key): val
for key, val in individual_factors.items()
}

# Override factors from environment if necessary
individual_factors.update(
{
key[len(_CACHE_PREFIX) + 1 :].lower(): float(val)
_canonicalise_cache_name(key[len(_CACHE_PREFIX) + 1 :]): float(val)
for key, val in self._environ.items()
if key.startswith(_CACHE_PREFIX + "_")
}
Expand All @@ -142,9 +176,9 @@ def read_config(self, config, **kwargs):
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(),)
"caches.per_cache_factors.%s must be a number" % (cache,)
)
self.cache_factors[cache.lower()] = factor
self.cache_factors[cache] = factor

# Resize all caches (if necessary) with the new factors we've loaded
self.resize_all_caches()
Expand Down
28 changes: 28 additions & 0 deletions tests/config/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,31 @@ def test_global_instantiated_after_config_load(self):
cache = LruCache(100)
add_resizable_cache("foo", cache_resize_callback=cache.set_cache_factor)
self.assertEqual(cache.max_size, 150)

def test_cache_with_asterisk_in_name(self):
"""Some caches have asterisks in their name, test that they are set correctly.
"""

config = {
"caches": {
"per_cache_factors": {"*cache_a*": 5, "cache_b": 6, "cache_c": 2}
}
}
t = TestConfig()
t.caches._environ = {
"SYNAPSE_CACHE_FACTOR_CACHE_A": "2",
"SYNAPSE_CACHE_FACTOR_CACHE_B": 3,
}
t.read_config(config, config_dir_path="", data_dir_path="")

cache_a = LruCache(100)
add_resizable_cache("*cache_a*", cache_resize_callback=cache_a.set_cache_factor)
self.assertEqual(cache_a.max_size, 200)

cache_b = LruCache(100)
add_resizable_cache("*Cache_b*", cache_resize_callback=cache_b.set_cache_factor)
self.assertEqual(cache_b.max_size, 300)

cache_c = LruCache(100)
add_resizable_cache("*cache_c*", cache_resize_callback=cache_c.set_cache_factor)
self.assertEqual(cache_c.max_size, 200)