mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-15 09:10:32 +00:00
Map Postgres errors onto a DBAPI2 exception hierarchy
Previously every `tokio_postgres` error became a bare `RuntimeError`. Synapse's
transaction driver, though, branches on the *type* of a database error and on
its `pgcode`: `new_transaction` retries `OperationalError`, retries deadlocks it
recognises via `is_deadlock` (which reads `pgcode`) on a `DatabaseError`, and
`simple_upsert` retries `IntegrityError`. With everything collapsed to
`RuntimeError` none of that fired.
Add just the distinctions Synapse acts on, rather than psycopg2's full PEP-249
hierarchy: `Error` -> `DatabaseError` -> {`OperationalError`, `IntegrityError`},
exposed on the `postgres` submodule, each instance tagged with `pgcode` (the
SQLSTATE string, or `None`). A small classifier maps the SQLSTATE class:
constraint violations (`23`) to `IntegrityError`, connection/resource classes
(`08`/`53`/`57`/`58`) to `OperationalError`, everything else (incl. `40*`
deadlocks, which retry via `pgcode`) to `DatabaseError`. Codeless errors are
split with `is_closed()`: a lost connection is operational, any other (a bad
parameter, a failed connect) is a plain `DatabaseError` so it isn't retried.
Errors surfacing while a result stream is drained (the usual case for an
`INSERT` constraint violation) now route through the same mapping, so they carry
the right class and `pgcode` too.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Erik Johnston
co-authored by
Claude Opus 4.8
parent
a2a994e0fd
commit
3f032ebb1a
@@ -19,18 +19,21 @@ use crate::tokio_runtime::runtime_handle;
|
||||
|
||||
mod connection;
|
||||
mod cursor_state;
|
||||
mod errors;
|
||||
mod helpers;
|
||||
mod libpq;
|
||||
mod value;
|
||||
|
||||
/// Register the `postgres` submodule (the `Connection` / `Cursor` classes and
|
||||
/// the `connect` factory) under the parent `database` module.
|
||||
/// Register the `postgres` submodule (the `Connection` / `Cursor` classes, the
|
||||
/// DBAPI2 exception hierarchy and the `connect` factory) under the parent
|
||||
/// `database` module.
|
||||
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
let child = PyModule::new(py, "postgres")?;
|
||||
|
||||
child.add_class::<connection::Connection>()?;
|
||||
child.add_class::<connection::Cursor>()?;
|
||||
child.add_function(wrap_pyfunction!(connect, &child)?)?;
|
||||
errors::register_exceptions(py, &child)?;
|
||||
|
||||
m.add_submodule(&child)?;
|
||||
|
||||
@@ -43,11 +46,6 @@ pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Map a [`tokio_postgres`] error into a Python `RuntimeError`.
|
||||
fn pg_err_to_py(e: tokio_postgres::Error) -> PyErr {
|
||||
PyRuntimeError::new_err(format!("postgres error: {e}"))
|
||||
}
|
||||
|
||||
/// Open a new Postgres connection from a libpq-style DSN.
|
||||
///
|
||||
/// Blocks until the connection is established, then spawns the long-lived
|
||||
|
||||
Reference in New Issue
Block a user