Files
synapse/synapse/synapse_rust/database/postgres.pyi
T
Erik JohnstonandClaude Opus 4.8 f2b41a6005 Add a connection-checkout timeout to the native Rust Postgres pool
A checkout from the Rust connection pool (`ConnectionPool.connect`) previously
blocked indefinitely while waiting for a free slot or establishing a new
connection, so an unreachable or overloaded server could hang the worker
threads handling requests forever.

Bound the whole checkout with a configurable timeout: deadpool's `wait` and
`create` timeouts (enabled via its `rt_tokio_1` runtime feature, driven on our
own tokio runtime). Hitting it raises an OperationalError, which Synapse
already treats as a retryable operational error.

Configurable via the `pool_checkout_timeout` database option (milliseconds;
default 30s, `0` disables), read by the Rust engine and plumbed to both the
connection pool and the bootstrap (pool-of-one) connection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3G4M92AmwSSZCbmtMJU3d
2026-10-12 18:31:24 +00:00

118 lines
4.4 KiB
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>.
from types import TracebackType
from typing import Any, Optional, Sequence
from synapse.synapse_rust.runtime import RustRuntime
class ConnectionPool:
"""A pool of native `tokio_postgres` connections.
Built once from a libpq-style DSN; connections are opened lazily. This is
the only way to obtain a :class:`Connection`.
"""
def __init__(
self,
runtime: RustRuntime,
dsn: str,
max_size: int = 10,
*,
synchronous_commit: bool = True,
statement_timeout_ms: int | None = None,
checkout_timeout_ms: int | None = None,
sslmode: str | None = None,
sslrootcert: str | None = None,
sslcert: str | None = None,
sslkey: str | None = None,
sslpassword: str | None = None,
) -> None:
"""
``runtime`` is the homeserver's ``RustRuntime`` (``hs.get_rust_runtime()``);
its tokio runtime is started on first use, so this works during schema
setup and in tests.
Each connection gets the same session setup when opened: the default
(REPEATABLE READ) isolation level, plus ``synchronous_commit`` off and
``statement_timeout`` (milliseconds) if configured.
``checkout_timeout_ms`` bounds how long ``connect`` may wait for a
connection; ``None`` or ``0`` waits indefinitely.
The ``ssl*`` keyword arguments are the libpq TLS settings; see
``rust/src/database/postgres/tls.rs``.
"""
def connect(self) -> Connection:
"""Check a connection out of the pool, blocking until one is available.
A failure to establish the connection raises the same DBAPI2 exceptions
a query error would.
"""
def close(self) -> None: ...
class Connection:
"""A connection checked out of a :class:`ConnectionPool`."""
@property
def autocommit(self) -> bool: ...
def cursor(self) -> Cursor: ...
def commit(self) -> None: ...
def rollback(self) -> None: ...
def close(self) -> None: ...
def discard(self) -> None:
"""Close the underlying session instead of returning it to the pool
(which shrinks, growing back on demand). For forced recycling, where a
fresh server session is wanted."""
def is_closed(self) -> bool: ...
def in_transaction(self) -> bool: ...
def set_autocommit(self, autocommit: bool) -> None: ...
def __enter__(self) -> Connection: ...
def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc: Optional[BaseException],
tb: Optional[TracebackType],
) -> bool: ...
class Cursor:
"""A PEP-249-style cursor over a connection's current transaction.
Result-fetching uses ``fetch_one`` / ``fetch_all`` / ``fetch_next_batch`` and
``rowcount`` / ``description`` as *methods* (not DBAPI2 properties); the
:class:`synapse.storage.rust_dbapi.Cursor` adapter presents the DBAPI2 shape.
"""
def execute(self, query: str, params: Optional[Sequence[Any]] = None) -> None: ...
def executemany(self, query: str, params_seq: Sequence[Sequence[Any]]) -> None: ...
def executescript(self, script: str) -> None: ...
def fetch_one(self) -> Optional[tuple[Any, ...]]: ...
def fetch_all(self) -> list[tuple[Any, ...]]: ...
def fetch_next_batch(self, capacity: int = 100) -> list[tuple[Any, ...]]: ...
def rowcount(self) -> int: ...
def description(self) -> Optional[list[tuple[Any, ...]]]: ...
def close(self) -> None: ...
# The PEP-249 exception hierarchy raised by the backend. `DatabaseError` and its
# subclasses carry the SQLSTATE as `pgcode` (a 5-character string, or `None`),
# matching psycopg2 so engine code such as `is_deadlock` can read `error.pgcode`.
class Error(Exception): ...
class DatabaseError(Error):
pgcode: Optional[str]
class OperationalError(DatabaseError): ...
class IntegrityError(DatabaseError): ...
class ProgrammingError(DatabaseError): ...