Fehler einer Quelle nicht mehr die Transaktion mitreissen lassen

Der Handler in durchlauf() fing den Fehler einer Quelle ab, damit sie nicht
den ganzen Durchlauf killt, liess die abgebrochene Postgres-Transaktion aber
stehen. Das notiere() unmittelbar danach lief in genau diese Transaktion und
warf InFailedSqlTransaction - ungefangen, also war der Prozess tot.

Doppelt aergerlich: der eigentliche Fehler stand nur in `fehler` und wurde
weder protokolliert noch ausgegeben, weil beides erst nach dem notiere()
kommt. Im Journal blieb allein die Folgewirkung sichtbar.

Beide Senken bekommen rollback(), der Fehlerpfad ruft es auf, und notiere()
selbst laeuft in einem eigenen try - ein fehlgeschlagenes Protokoll ist ein
Grund fuer eine Meldung, nicht fuer einen Abbruch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013vHZCwcCJnz4LKNgvT4bxv
This commit is contained in:
irrlicht
2026-09-06 12:51:13 +02:00
co-authored by Claude Opus 5
parent 5a9bbe6e92
commit ab20fa5d15
+17 -2
View File
@@ -298,6 +298,9 @@ class JsonlSenke:
def zustand(self, qid): def zustand(self, qid):
return None, None return None, None
def rollback(self):
self.db.rollback()
def schreibe(self, quelle, items, slot): def schreibe(self, quelle, items, slot):
frisch, neu, geaendert = [], 0, 0 frisch, neu, geaendert = [], 0, 0
for it in items: for it in items:
@@ -357,6 +360,9 @@ class PostgresSenke:
import psycopg import psycopg
self.conn = psycopg.connect(dsn, autocommit=False) self.conn = psycopg.connect(dsn, autocommit=False)
def rollback(self):
self.conn.rollback()
def sync_quellen(self, quellen): def sync_quellen(self, quellen):
with self.conn.cursor() as cur: with self.conn.cursor() as cur:
for q in quellen: for q in quellen:
@@ -553,9 +559,18 @@ def durchlauf(quellen, senke, slot):
except Exception as e: # eine Quelle darf nicht except Exception as e: # eine Quelle darf nicht
fehler = f"{type(e).__name__}: {e}" # den Durchlauf killen fehler = f"{type(e).__name__}: {e}" # den Durchlauf killen
status = getattr(e, "code", None) status = getattr(e, "code", None)
# Ohne Rollback bleibt eine abgebrochene Transaktion stehen, und
# das notiere() gleich darunter scheitert an ihr statt den Fehler
# festzuhalten - der eigentliche Grund waere dann verloren.
senke.rollback()
dauer = int((time.monotonic() - t0) * 1000) dauer = int((time.monotonic() - t0) * 1000)
senke.notiere(q["id"], slot, dauer, status, gesehen, neu, geaendert, try:
uebersprungen, bytes_roh, fehler) senke.notiere(q["id"], slot, dauer, status, gesehen, neu, geaendert,
uebersprungen, bytes_roh, fehler)
except Exception as e: # das Protokoll darf den Lauf nicht killen
senke.rollback()
print(f" ! {q['id']:14} Protokoll fehlgeschlagen: "
f"{type(e).__name__}: {e}", flush=True)
ges_neu += neu ges_neu += neu
ges_geaendert += geaendert ges_geaendert += geaendert
ges_gesehen += gesehen ges_gesehen += gesehen