Files
synapse/tests/config/test_task_scheduler_config.py
T
NEVIL ANSON DSOUZAandDevon Hudson 1d7880b9cb Make task scheduler concurrency configurable (#18308) (#20230)
### Summary
Resolves #18308.

Makes the `TaskScheduler`'s maximum concurrent running tasks
configurable via `task_scheduler.max_concurrent_tasks` in the homeserver
configuration and defaults it to `2` (reduced from the previous
hardcoded limit of `5`).

### Motivation
Twisted's `adbapi.ConnectionPool` defaults to 5 database connections
(`cp_max=5`). When the `TaskScheduler` previously executed up to 5 tasks
concurrently, it could exhaust the entire database connection pool,
starving regular API and synchronization requests on smaller homeserver
instances. Lowering the default to 2 preserves at least 3 connections
for foreground requests while allowing concurrent tasks to progress, and
gives administrators the ability to tune the limit according to their
host and connection pool sizing.

### Changes
- Added `TaskSchedulerConfig` (`task_scheduler.max_concurrent_tasks`)
with validation (must be a positive integer) and registered it in
`HomeServerConfig`.
- Updated `TaskScheduler` to enforce the configured concurrency limit
and updated class default to 2.
- Updated config documentation and JSON schema.
- Added config tests in `tests/config/test_task_scheduler_config.py` and
updated unit tests in `tests/util/test_task_scheduler.py`.
- Added newsfragment in `changelog.d/18308.feature`.

---------

Signed-off-by: nevil06 <nevilansondsouza@gmail.com>
Co-authored-by: Devon Hudson <devonhudson@librem.one>
2026-09-22 21:33:59 +00:00

55 lines
2.1 KiB
Python

#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright 2026 Nevil Anson Dsouza
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#
#
from synapse.config._base import ConfigError, RootConfig
from synapse.config.homeserver import HomeServerConfig
from synapse.config.task_scheduler import TaskSchedulerConfig
from tests.unittest import TestCase
from tests.utils import default_config
class TaskSchedulerConfigTestCase(TestCase):
def test_default_configuration(self) -> None:
config_dict = default_config(server_name="test")
config = HomeServerConfig()
config.parse_config_dict(config_dict, "", "")
self.assertEqual(config.task_scheduler.max_concurrent_tasks, 5)
def test_custom_configuration(self) -> None:
config_dict = default_config(server_name="test")
config_dict["task_scheduler"] = {"max_concurrent_tasks": 4}
config = HomeServerConfig()
config.parse_config_dict(config_dict, "", "")
self.assertEqual(config.task_scheduler.max_concurrent_tasks, 4)
def test_invalid_configuration(self) -> None:
config = TaskSchedulerConfig(RootConfig())
with self.assertRaises(ConfigError):
config.read_config({"task_scheduler": {"max_concurrent_tasks": 0}})
with self.assertRaises(ConfigError):
config.read_config({"task_scheduler": {"max_concurrent_tasks": -1}})
with self.assertRaises(ConfigError):
config.read_config(
{"task_scheduler": {"max_concurrent_tasks": "not-an-int"}}
)
with self.assertRaises(ConfigError):
config.read_config({"task_scheduler": {"max_concurrent_tasks": True}})
with self.assertRaises(ConfigError):
config.read_config({"task_scheduler": "not-a-dict"})