Bug: Kein Unique Constraint auf (source, source_id) — doppelte Tickets möglich #76

Open
opened 2026-03-30 20:19:31 +00:00 by David · 0 comments
Collaborator

Beschreibung

Das Ticket-Model hat keinen Unique Constraint auf der Kombination (source, source_id). Wenn Odoo oder AppSignal die gleiche ID zweimal liefert (z.B. bei Polling-Overlap), entstehen Duplikate in der Datenbank.

Hintergrund

odoo_id ist zwar unique=True, aber source_id (allgemein) hat keinen solchen Constraint. Bei AppSignal-Tickets oder zukünftigen Quellen fehlt der Schutz komplett.

Akzeptanzkriterien

  • Unique Constraint auf (source, source_id) wenn beide nicht NULL
  • Duplikat-Insert gibt klaren Fehler statt silent fail
  • Polling-Code fängt IntegrityError ab und überspringt Duplikate
  • Test: Zweiter Insert mit gleicher source+source_id wird abgelehnt

Technische Hinweise

  • Betroffene Dateien: backend/models/ticket.py, neue Alembic-Migration
  • Ansatz: UniqueConstraint('source', 'source_id', name='uq_ticket_source')
  • Migration nötig: ja (evtl. vorher Duplikate bereinigen)

Aufwand: S

## Beschreibung Das Ticket-Model hat keinen Unique Constraint auf der Kombination `(source, source_id)`. Wenn Odoo oder AppSignal die gleiche ID zweimal liefert (z.B. bei Polling-Overlap), entstehen Duplikate in der Datenbank. ## Hintergrund `odoo_id` ist zwar `unique=True`, aber `source_id` (allgemein) hat keinen solchen Constraint. Bei AppSignal-Tickets oder zukünftigen Quellen fehlt der Schutz komplett. ## Akzeptanzkriterien - [ ] Unique Constraint auf `(source, source_id)` wenn beide nicht NULL - [ ] Duplikat-Insert gibt klaren Fehler statt silent fail - [ ] Polling-Code fängt `IntegrityError` ab und überspringt Duplikate - [ ] Test: Zweiter Insert mit gleicher source+source_id wird abgelehnt ## Technische Hinweise - Betroffene Dateien: `backend/models/ticket.py`, neue Alembic-Migration - Ansatz: `UniqueConstraint('source', 'source_id', name='uq_ticket_source')` - Migration nötig: ja (evtl. vorher Duplikate bereinigen) ## Aufwand: S
Sign in to join this conversation.
No description provided.