mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-12 09:58:24 +00:00
Add /createRoom benchmarks and fix the template DB under the Rust driver
`tests/rest/client/bench_createroom.py` times room creation per room in the trial harness, with optional cProfile dumps and per-room counts of database interactions and SQL statements. It skips unless `SYNAPSE_BENCH_CREATEROOM` is set. `scripts-dev/createroom_bench.py` times it against a real server, reusing `persist_bench.Server`. That server now lifts the room-creation rate limit and can run on psycopg2 instead of the native driver. The test template database's maintenance connection now always uses psycopg2, as `setup_test_homeserver` does. The Rust driver's `postgres` module has no `connect`, so it failed under `SYNAPSE_TEST_RUST_DRIVER=1`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVLsNh6akt4ifEizY4D1ta
This commit is contained in:
1 parent
cac962a4a7
commit
4dd87ff58a
5 files changed
+336
-5
No files matched your search
Executable
+139
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
#
|
||||
# Copyright (C) 2026 Element Creations Ltd
|
||||
#
|
||||
# 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>.
|
||||
#
|
||||
|
||||
"""Time `/createRoom` against a real Synapse on Postgres.
|
||||
|
||||
Starts the server the way `persist_bench.py` does, once per
|
||||
`rust_event_persistence` level. For each scenario it creates five warm-up
|
||||
rooms, then `--rooms` rooms one after another, and reports the mean and median
|
||||
request time and the CPU of the reactor thread and of the process per room.
|
||||
|
||||
poetry run python scripts-dev/createroom_bench.py --levels off,full
|
||||
|
||||
`--psycopg2` runs the server on psycopg2 instead of the native driver.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import statistics
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
import persist_bench as pb # noqa: E402
|
||||
|
||||
SCENARIOS: dict[str, dict[str, Any]] = {
|
||||
"plain": {},
|
||||
"private_invite": {"preset": "private_chat", "invite": ["@invitee:bench"]},
|
||||
"public_named": {"preset": "public_chat", "name": "Bench room", "topic": "A topic"},
|
||||
"initial_state": {
|
||||
"initial_state": [
|
||||
{
|
||||
"type": "m.room.encryption",
|
||||
"state_key": "",
|
||||
"content": {"algorithm": "m.megolm.v1.aes-sha2"},
|
||||
},
|
||||
{
|
||||
"type": "m.room.guest_access",
|
||||
"state_key": "",
|
||||
"content": {"guest_access": "can_join"},
|
||||
},
|
||||
{
|
||||
"type": "m.space.child",
|
||||
"state_key": "!a:bench",
|
||||
"content": {"via": ["bench"]},
|
||||
},
|
||||
{
|
||||
"type": "m.space.child",
|
||||
"state_key": "!b:bench",
|
||||
"content": {"via": ["bench"]},
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
CREATE_ROOM = "/_matrix/client/v3/createRoom"
|
||||
|
||||
|
||||
def bench_level(level: str, rooms: int, rust_driver: bool) -> list[dict[str, Any]]:
|
||||
results = []
|
||||
workdir = tempfile.mkdtemp(prefix=f"createroom-bench-{level}-")
|
||||
server = pb.Server(level, workdir, rust_driver=rust_driver)
|
||||
try:
|
||||
server.start()
|
||||
token = pb.register("creator")
|
||||
pb.register("invitee")
|
||||
for name, body in SCENARIOS.items():
|
||||
for _ in range(5):
|
||||
pb.ok("POST", CREATE_ROOM, body, token)
|
||||
before = server.sample()
|
||||
times = []
|
||||
for _ in range(rooms):
|
||||
t0 = time.perf_counter()
|
||||
pb.ok("POST", CREATE_ROOM, body, token)
|
||||
times.append(time.perf_counter() - t0)
|
||||
after = server.sample()
|
||||
row = {
|
||||
"level": level,
|
||||
"scenario": name,
|
||||
"mean_ms": 1000 * statistics.mean(times),
|
||||
"p50_ms": 1000 * statistics.median(times),
|
||||
"reactor_cpu_ms": 1000
|
||||
* (after.reactor_cpu - before.reactor_cpu)
|
||||
/ rooms,
|
||||
"process_cpu_ms": 1000
|
||||
* (after.process_cpu - before.process_cpu)
|
||||
/ rooms,
|
||||
}
|
||||
results.append(row)
|
||||
print(row, flush=True)
|
||||
finally:
|
||||
server.stop()
|
||||
return results
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--levels", default="off,full")
|
||||
parser.add_argument("--rooms", type=int, default=40)
|
||||
parser.add_argument("--psycopg2", action="store_true")
|
||||
parser.add_argument("--json", help="also write the results to this file")
|
||||
args = parser.parse_args()
|
||||
|
||||
results = []
|
||||
for level in args.levels.split(","):
|
||||
results += bench_level(level, args.rooms, rust_driver=not args.psycopg2)
|
||||
|
||||
print(
|
||||
f"\n{'scenario':<16} {'level':<12} {'mean ms':>8} {'p50 ms':>8}"
|
||||
f" {'reactor ms':>11} {'process ms':>11}"
|
||||
)
|
||||
for r in results:
|
||||
print(
|
||||
f"{r['scenario']:<16} {r['level']:<12} {r['mean_ms']:>8.1f}"
|
||||
f" {r['p50_ms']:>8.1f} {r['reactor_cpu_ms']:>11.1f}"
|
||||
f" {r['process_cpu_ms']:>11.1f}"
|
||||
)
|
||||
if args.json:
|
||||
with open(args.json, "w") as f:
|
||||
json.dump(results, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -120,9 +120,10 @@ def _metrics() -> dict[str, float]:
|
||||
class Server:
|
||||
"""A Synapse process on its own database."""
|
||||
|
||||
def __init__(self, level: str, workdir: str) -> None:
|
||||
def __init__(self, level: str, workdir: str, rust_driver: bool = True) -> None:
|
||||
self.level = level
|
||||
self.workdir = workdir
|
||||
self.rust_driver = rust_driver
|
||||
self.database = f"persist_bench_{level}_{secrets.token_hex(3)}"
|
||||
self.process: subprocess.Popen | None = None
|
||||
|
||||
@@ -153,7 +154,7 @@ class Server:
|
||||
],
|
||||
"database": {
|
||||
"name": "psycopg2",
|
||||
"use_rust_driver": True,
|
||||
"use_rust_driver": self.rust_driver,
|
||||
"args": {
|
||||
"database": self.database,
|
||||
"host": os.environ.get("PGHOST", "localhost"),
|
||||
@@ -171,6 +172,7 @@ class Server:
|
||||
},
|
||||
"rc_joins_per_room": {"per_second": 100000, "burst_count": 100000},
|
||||
"rc_registration": {"per_second": 100000, "burst_count": 100000},
|
||||
"rc_room_creation": {"per_second": 100000, "burst_count": 100000},
|
||||
"rc_login": {
|
||||
"address": {"per_second": 100000, "burst_count": 100000},
|
||||
"account": {"per_second": 100000, "burst_count": 100000},
|
||||
|
||||
Reference in new issue
Block a user