67 lines
1.8 KiB
Python
67 lines
1.8 KiB
Python
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
from threading import RLock
|
|
|
|
import psycopg as pg
|
|
|
|
from app.core.config import get_pgconn_string
|
|
|
|
g_conn_dict: dict[str, pg.Connection] = {}
|
|
_registry_lock = RLock()
|
|
_project_locks: dict[str, RLock] = {}
|
|
|
|
|
|
def _is_closed(connection: pg.Connection) -> bool:
|
|
return bool(getattr(connection, "closed", False))
|
|
|
|
|
|
def _close_connection(connection: pg.Connection) -> None:
|
|
if not _is_closed(connection):
|
|
connection.close()
|
|
|
|
|
|
def _get_project_lock(name: str) -> RLock:
|
|
with _registry_lock:
|
|
lock = _project_locks.get(name)
|
|
if lock is None:
|
|
lock = RLock()
|
|
_project_locks[name] = lock
|
|
return lock
|
|
|
|
|
|
def open_connection(name: str) -> pg.Connection:
|
|
with _get_project_lock(name):
|
|
connection = g_conn_dict.get(name)
|
|
if connection is None or _is_closed(connection):
|
|
if connection is not None:
|
|
_close_connection(connection)
|
|
connection = pg.connect(
|
|
conninfo=get_pgconn_string(db_name=name), autocommit=True
|
|
)
|
|
g_conn_dict[name] = connection
|
|
return connection
|
|
|
|
|
|
def is_connection_open(name: str) -> bool:
|
|
with _get_project_lock(name):
|
|
connection = g_conn_dict.get(name)
|
|
if connection is None:
|
|
return False
|
|
if _is_closed(connection):
|
|
del g_conn_dict[name]
|
|
return False
|
|
return True
|
|
|
|
|
|
def close_connection(name: str) -> None:
|
|
with _get_project_lock(name):
|
|
connection = g_conn_dict.pop(name, None)
|
|
if connection is not None:
|
|
_close_connection(connection)
|
|
|
|
|
|
@contextmanager
|
|
def project_connection(name: str) -> Iterator[pg.Connection]:
|
|
with _get_project_lock(name):
|
|
yield open_connection(name)
|