From 6585598fca5d015879216b91124b16d91392fb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 17 Jun 2026 18:50:33 +0400 Subject: [PATCH] =?UTF-8?q?=D0=BF=D1=83=D0=BB:=20keepalive,=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D0=B6=D0=B8=D0=B2=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8,=20min=3D2=20max=3D5,=20reconnect,=20v1.22?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 6 ++-- site/db.py | 64 +++++++++++++++++++++----------------- site/templates/upload.html | 2 +- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/site/app.py b/site/app.py index 0196697..d8c5da6 100644 --- a/site/app.py +++ b/site/app.py @@ -152,6 +152,7 @@ class ContractsApp: mime = mimeutil.guess_mime(filename) or "application/octet-stream" cid = data.get("cid") + conn = None if cid: contract_id = cid else: @@ -165,10 +166,11 @@ class ContractsApp: ) contract_id = cur.fetchone()[0] conn.commit() + + doc_id = _save_file_to_db(filename, file_bytes, mime, str(contract_id), conn=conn) + if conn: cur.close() db.put_conn(conn) - - doc_id = _save_file_to_db(filename, file_bytes, mime, str(contract_id)) return jsonify({"contract_id": str(contract_id), "doc_id": str(doc_id) if doc_id else None}) # ── Чанковая загрузка ────────────────────────────────────── diff --git a/site/db.py b/site/db.py index ff956c2..6ff69b7 100644 --- a/site/db.py +++ b/site/db.py @@ -1,39 +1,41 @@ """ -db.py — Транспортный слой к базе данных (с пулом соединений). - -Пул psycopg2.pool.ThreadedConnectionPool (min=1, max=3). -Все query/execute забирают соединение из пула и возвращают обратно. +db.py — Пул соединений с keepalive, проверкой живости, переподключением. """ import os +import logging from psycopg2 import pool as _pgpool +from psycopg2 import OperationalError, InterfaceError import psycopg2 +logger = logging.getLogger(__name__) + _pool = None def _get_pool(): - """Ленивая инициализация пула.""" global _pool if _pool is None: _pool = _pgpool.ThreadedConnectionPool( - minconn=1, - maxconn=3, + minconn=2, + maxconn=5, host=os.getenv("DB_HOST"), port=os.getenv("DB_PORT", 5432), dbname=os.getenv("DB_NAME"), user=os.getenv("DB_USER"), password=os.getenv("DB_PASS"), sslmode=os.getenv("DB_SSLMODE", "disable"), + connect_timeout=10, + keepalives=1, + keepalives_idle=30, + keepalives_interval=10, + keepalives_count=3, ) return _pool def _pg_connect(dbname): - """ - Сырое подключение к ЛЮБОЙ базе (для /test createdb). - НЕ из пула — для создания БД нужна отдельная сессия. - """ + """Сырое подключение к ЛЮБОЙ базе (для /test createdb).""" return psycopg2.connect( host=os.getenv("DB_HOST"), port=os.getenv("DB_PORT", 5432), @@ -45,14 +47,28 @@ def _pg_connect(dbname): def connect(): - """ - Подключение к ЦЕЛЕВОЙ базе из пула. - Возвращает (connection, None) или (None, error). - """ - try: - return _get_pool().getconn(), None - except Exception as e: - return None, str(e) + """Подключение из пула с проверкой живости.""" + pool = _get_pool() + for attempt in range(3): + try: + conn = pool.getconn() + # Проверить живость + cur = conn.cursor() + cur.execute("SELECT 1") + cur.close() + return conn, None + except (OperationalError, InterfaceError) as e: + if conn: + try: pool.putconn(conn, close=True) + except: pass + if attempt == 2: + return None, str(e) + except Exception as e: + if conn: + try: pool.putconn(conn) + except: pass + return None, str(e) + return None, "pool exhausted" def put_conn(conn): @@ -64,10 +80,6 @@ def put_conn(conn): def query(sql_text, params=None): - """ - SELECT → (result, None) или (None, error). - result = {"columns": [...], "rows": [[...], ...]} - """ conn, err = connect() if err: return None, f"connect: {err}" @@ -85,9 +97,6 @@ def query(sql_text, params=None): def execute(sql_text, params=None): - """ - INSERT/UPDATE/DELETE → (rowcount, None) или (None, error). - """ conn, err = connect() if err: return None, f"connect: {err}" @@ -107,9 +116,6 @@ def execute(sql_text, params=None): def query_one(sql_text, params=None): - """ - SELECT одной строки → (row_dict, None) или (None, error). - """ result, err = query(sql_text, params) if err: return None, err diff --git a/site/templates/upload.html b/site/templates/upload.html index dcd663b..b187027 100644 --- a/site/templates/upload.html +++ b/site/templates/upload.html @@ -59,7 +59,7 @@
Nubes - Сверка договоров v1.21 + Сверка договоров v1.22