fix(db): upsert_node Race-Condition behoben (UNIQUE constraint)

INSERT OR IGNORE + UPDATE statt SELECT → INSERT eliminiert den
UNIQUE-constraint-Fehler bei konkurrierenden async-Aufrufen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ppfeiffer 2026-02-20 22:08:10 +01:00
parent fd9eb99b6a
commit b431797d32
3 changed files with 21 additions and 24 deletions

View file

@ -1,5 +1,12 @@
# Changelog
## [0.08.20] - 2026-02-20
### Fixed
- **`upsert_node` Race-Condition** (`UNIQUE constraint failed: nodes.node_id`):
Statt SELECT → INSERT nutzt die Methode jetzt `INSERT OR IGNORE` + `UPDATE`,
wodurch konkurrierende Aufrufe keinen Constraint-Fehler mehr auslösen.
## [0.08.19] - 2026-02-20
### Added

View file

@ -1,4 +1,4 @@
version: "0.08.19"
version: "0.08.20"
bot:
name: "MeshDD-Bot"

View file

@ -129,29 +129,19 @@ class Database:
async def upsert_node(self, node_id: str, **kwargs) -> dict:
now = time.time()
existing = await self.get_node(node_id)
if existing:
# Row anlegen falls nicht vorhanden (first_seen nur beim ersten Mal gesetzt)
await self.db.execute(
"INSERT OR IGNORE INTO nodes (node_id, first_seen, last_seen) VALUES (?, ?, ?)",
(node_id, now, now),
)
# Nicht-None-Felder + last_seen immer aktualisieren
updates = {k: v for k, v in kwargs.items() if v is not None}
if not updates:
return dict(existing)
updates["last_seen"] = now
set_clause = ", ".join(f"{k} = ?" for k in updates)
values = list(updates.values()) + [node_id]
await self.db.execute(
f"UPDATE nodes SET {set_clause} WHERE node_id = ?", values
)
else:
kwargs["node_id"] = node_id
kwargs.setdefault("first_seen", now)
kwargs["last_seen"] = now
cols = ", ".join(kwargs.keys())
placeholders = ", ".join("?" for _ in kwargs)
await self.db.execute(
f"INSERT INTO nodes ({cols}) VALUES ({placeholders})",
list(kwargs.values()),
)
await self.db.commit()
return dict(await self.get_node(node_id))