diff options
author | Mathieu Velten <matmaul@gmail.com> | 2024-03-14 14:49:54 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-14 13:49:54 +0000 |
commit | cb562d73aaa592c05b96672724ba296d02cee896 (patch) | |
tree | eead02d23e1dbb580e0a7410894be84d02d70ddb /tests/utils.py | |
parent | Bump types-psycopg2 from 2.9.21.16 to 2.9.21.20240311 (#16995) (diff) | |
download | synapse-cb562d73aaa592c05b96672724ba296d02cee896.tar.xz |
Improve lock performance when a lot of locks are waiting (#16840)
When a lot of locks are waiting for a single lock, notifying all locks independently with `call_later` on each release is really costly and incurs some kind of async contention, where the CPU is spinning a lot for not much. The included test is taking around 30s before the change, and 0.5s after. It was found following failing tests with https://github.com/element-hq/synapse/pull/16827.
Diffstat (limited to '')
-rw-r--r-- | tests/utils.py | 42 |
1 files changed, 41 insertions, 1 deletions
diff --git a/tests/utils.py b/tests/utils.py index 757320ebee..9fd26ef348 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -21,7 +21,20 @@ import atexit import os -from typing import Any, Callable, Dict, List, Tuple, Type, TypeVar, Union, overload +import signal +from types import FrameType, TracebackType +from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) import attr from typing_extensions import Literal, ParamSpec @@ -379,3 +392,30 @@ def checked_cast(type: Type[T], x: object) -> T: """ assert isinstance(x, type) return x + + +class TestTimeout(Exception): + pass + + +class test_timeout: + def __init__(self, seconds: int, error_message: Optional[str] = None) -> None: + if error_message is None: + error_message = "test timed out after {}s.".format(seconds) + self.seconds = seconds + self.error_message = error_message + + def handle_timeout(self, signum: int, frame: Optional[FrameType]) -> None: + raise TestTimeout(self.error_message) + + def __enter__(self) -> None: + signal.signal(signal.SIGALRM, self.handle_timeout) + signal.alarm(self.seconds) + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + signal.alarm(0) |