пул: keepalive, проверка живости, min=2 max=5, reconnect, v1.22
This commit is contained in:
+4
-2
@@ -152,6 +152,7 @@ class ContractsApp:
|
|||||||
mime = mimeutil.guess_mime(filename) or "application/octet-stream"
|
mime = mimeutil.guess_mime(filename) or "application/octet-stream"
|
||||||
cid = data.get("cid")
|
cid = data.get("cid")
|
||||||
|
|
||||||
|
conn = None
|
||||||
if cid:
|
if cid:
|
||||||
contract_id = cid
|
contract_id = cid
|
||||||
else:
|
else:
|
||||||
@@ -165,10 +166,11 @@ class ContractsApp:
|
|||||||
)
|
)
|
||||||
contract_id = cur.fetchone()[0]
|
contract_id = cur.fetchone()[0]
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
doc_id = _save_file_to_db(filename, file_bytes, mime, str(contract_id), conn=conn)
|
||||||
|
if conn:
|
||||||
cur.close()
|
cur.close()
|
||||||
db.put_conn(conn)
|
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})
|
return jsonify({"contract_id": str(contract_id), "doc_id": str(doc_id) if doc_id else None})
|
||||||
|
|
||||||
# ── Чанковая загрузка ──────────────────────────────────────
|
# ── Чанковая загрузка ──────────────────────────────────────
|
||||||
|
|||||||
+35
-29
@@ -1,39 +1,41 @@
|
|||||||
"""
|
"""
|
||||||
db.py — Транспортный слой к базе данных (с пулом соединений).
|
db.py — Пул соединений с keepalive, проверкой живости, переподключением.
|
||||||
|
|
||||||
Пул psycopg2.pool.ThreadedConnectionPool (min=1, max=3).
|
|
||||||
Все query/execute забирают соединение из пула и возвращают обратно.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from psycopg2 import pool as _pgpool
|
from psycopg2 import pool as _pgpool
|
||||||
|
from psycopg2 import OperationalError, InterfaceError
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_pool = None
|
_pool = None
|
||||||
|
|
||||||
|
|
||||||
def _get_pool():
|
def _get_pool():
|
||||||
"""Ленивая инициализация пула."""
|
|
||||||
global _pool
|
global _pool
|
||||||
if _pool is None:
|
if _pool is None:
|
||||||
_pool = _pgpool.ThreadedConnectionPool(
|
_pool = _pgpool.ThreadedConnectionPool(
|
||||||
minconn=1,
|
minconn=2,
|
||||||
maxconn=3,
|
maxconn=5,
|
||||||
host=os.getenv("DB_HOST"),
|
host=os.getenv("DB_HOST"),
|
||||||
port=os.getenv("DB_PORT", 5432),
|
port=os.getenv("DB_PORT", 5432),
|
||||||
dbname=os.getenv("DB_NAME"),
|
dbname=os.getenv("DB_NAME"),
|
||||||
user=os.getenv("DB_USER"),
|
user=os.getenv("DB_USER"),
|
||||||
password=os.getenv("DB_PASS"),
|
password=os.getenv("DB_PASS"),
|
||||||
sslmode=os.getenv("DB_SSLMODE", "disable"),
|
sslmode=os.getenv("DB_SSLMODE", "disable"),
|
||||||
|
connect_timeout=10,
|
||||||
|
keepalives=1,
|
||||||
|
keepalives_idle=30,
|
||||||
|
keepalives_interval=10,
|
||||||
|
keepalives_count=3,
|
||||||
)
|
)
|
||||||
return _pool
|
return _pool
|
||||||
|
|
||||||
|
|
||||||
def _pg_connect(dbname):
|
def _pg_connect(dbname):
|
||||||
"""
|
"""Сырое подключение к ЛЮБОЙ базе (для /test createdb)."""
|
||||||
Сырое подключение к ЛЮБОЙ базе (для /test createdb).
|
|
||||||
НЕ из пула — для создания БД нужна отдельная сессия.
|
|
||||||
"""
|
|
||||||
return psycopg2.connect(
|
return psycopg2.connect(
|
||||||
host=os.getenv("DB_HOST"),
|
host=os.getenv("DB_HOST"),
|
||||||
port=os.getenv("DB_PORT", 5432),
|
port=os.getenv("DB_PORT", 5432),
|
||||||
@@ -45,14 +47,28 @@ def _pg_connect(dbname):
|
|||||||
|
|
||||||
|
|
||||||
def connect():
|
def connect():
|
||||||
"""
|
"""Подключение из пула с проверкой живости."""
|
||||||
Подключение к ЦЕЛЕВОЙ базе из пула.
|
pool = _get_pool()
|
||||||
Возвращает (connection, None) или (None, error).
|
for attempt in range(3):
|
||||||
"""
|
try:
|
||||||
try:
|
conn = pool.getconn()
|
||||||
return _get_pool().getconn(), None
|
# Проверить живость
|
||||||
except Exception as e:
|
cur = conn.cursor()
|
||||||
return None, str(e)
|
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):
|
def put_conn(conn):
|
||||||
@@ -64,10 +80,6 @@ def put_conn(conn):
|
|||||||
|
|
||||||
|
|
||||||
def query(sql_text, params=None):
|
def query(sql_text, params=None):
|
||||||
"""
|
|
||||||
SELECT → (result, None) или (None, error).
|
|
||||||
result = {"columns": [...], "rows": [[...], ...]}
|
|
||||||
"""
|
|
||||||
conn, err = connect()
|
conn, err = connect()
|
||||||
if err:
|
if err:
|
||||||
return None, f"connect: {err}"
|
return None, f"connect: {err}"
|
||||||
@@ -85,9 +97,6 @@ def query(sql_text, params=None):
|
|||||||
|
|
||||||
|
|
||||||
def execute(sql_text, params=None):
|
def execute(sql_text, params=None):
|
||||||
"""
|
|
||||||
INSERT/UPDATE/DELETE → (rowcount, None) или (None, error).
|
|
||||||
"""
|
|
||||||
conn, err = connect()
|
conn, err = connect()
|
||||||
if err:
|
if err:
|
||||||
return None, f"connect: {err}"
|
return None, f"connect: {err}"
|
||||||
@@ -107,9 +116,6 @@ def execute(sql_text, params=None):
|
|||||||
|
|
||||||
|
|
||||||
def query_one(sql_text, params=None):
|
def query_one(sql_text, params=None):
|
||||||
"""
|
|
||||||
SELECT одной строки → (row_dict, None) или (None, error).
|
|
||||||
"""
|
|
||||||
result, err = query(sql_text, params)
|
result, err = query(sql_text, params)
|
||||||
if err:
|
if err:
|
||||||
return None, err
|
return None, err
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="topbar">
|
<div class="topbar">
|
||||||
<img src="{{ url_for('static', filename='nubes-logo.svg') }}" alt="Nubes">
|
<img src="{{ url_for('static', filename='nubes-logo.svg') }}" alt="Nubes">
|
||||||
<span class="title">Сверка договоров <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.21</span></span>
|
<span class="title">Сверка договоров <span style="font-weight:400;color:var(--muted);font-size:12px;">v1.22</span></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
Reference in New Issue
Block a user