notebookvb

This commit is contained in:
Vladimir Buzalka
2026-06-15 18:07:32 +02:00
parent 8142de5216
commit 79216dfbdb
4 changed files with 74 additions and 5 deletions
Binary file not shown.
Binary file not shown.
+66
View File
@@ -31,6 +31,72 @@ def get_medicus_connection():
return fdb.connect(dsn=dsn, user="SYSDBA", password="masterkey", charset="win1250") return fdb.connect(dsn=dsn, user="SYSDBA", password="masterkey", charset="win1250")
class ReconnectingConnection:
"""Obal nad fdb.Connection, který se sám znovu připojí, když spojení umře.
Dlouho běžící procesy (např. MCP server) drží jedno spojení od startu.
To se rozpadne, když:
* se notebook uspí/hibernuje (TCP socket odumře), nebo
* na serveru proběhne denní gbak restore (Firebird zabije stará spojení).
Výsledkem je `SQLCODE -902 Error writing data to the connection`.
Tato třída ověří před každým použitím, že spojení žije, a když ne,
tiše ho znovu naváže. Volající kód používá .cursor()/.commit() beze změny.
"""
def __init__(self, connect_fn=get_medicus_connection):
self._connect = connect_fn
self._conn = None
def _alive(self):
if self._conn is None:
return False
try:
cur = self._conn.cursor()
cur.execute("SELECT 1 FROM RDB$DATABASE")
cur.fetchone()
return True
except Exception:
return False
def _ensure(self):
if not self._alive():
if self._conn is not None:
try:
self._conn.close()
except Exception:
pass
self._conn = None
self._conn = self._connect()
return self._conn
def cursor(self):
return self._ensure().cursor()
def commit(self):
return self._ensure().commit()
def rollback(self):
if self._conn is not None:
return self._conn.rollback()
def close(self):
if self._conn is not None:
try:
self._conn.close()
finally:
self._conn = None
def __getattr__(self, name):
# ostatní atributy/metody proxy na živé spojení
return getattr(self._ensure(), name)
def get_medicus_connection_reconnecting():
"""Vrátí spojení s automatickým reconnectem (vhodné pro dlouho běžící procesy)."""
return ReconnectingConnection(get_medicus_connection)
def get_medicus_db(): def get_medicus_db():
"""Vrátí MedicusDB instanci s připojením podle názvu počítače.""" """Vrátí MedicusDB instanci s připojením podle názvu počítače."""
conn = get_medicus_connection() conn = get_medicus_connection()
+8 -5
View File
@@ -12,20 +12,23 @@ from typing import Optional
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
sys.path.insert(0, str(Path(__file__).resolve().parent)) sys.path.insert(0, str(Path(__file__).resolve().parent))
from Knihovny.medicus_db import get_medicus_connection from Knihovny.medicus_db import get_medicus_connection_reconnecting
# Všechny logy MUSÍ jít na stderr — stdout je rezervován pro JSON-RPC # Všechny logy MUSÍ jít na stderr — stdout je rezervován pro JSON-RPC
def log(msg: str): def log(msg: str):
print(msg, file=sys.stderr, flush=True) print(msg, file=sys.stderr, flush=True)
# Připojení k Firebirdu # Připojení k Firebirdu — lazy + auto-reconnect.
# Spojení se naváže až při prvním dotazu a samo se obnoví, když umře
# (uspání notebooku, denní gbak restore na serveru). Proto nepadáme při startu,
# i kdyby DB zrovna nebyla dostupná.
conn = get_medicus_connection_reconnecting()
try: try:
conn = get_medicus_connection() conn.cursor().execute("SELECT 1 FROM RDB$DATABASE")
log("✓ Připojeno k Firebirdu (Medicus)") log("✓ Připojeno k Firebirdu (Medicus)")
except Exception as e: except Exception as e:
log(f"✗ Chyba připojení k Firebirdu: {e}") log(f"⚠ Firebird zatím nedostupný, zkusím se připojit při prvním dotazu: {e}")
sys.exit(1)
def rows_to_json(rows, description): def rows_to_json(rows, description):