Offline-Warteschlange: doppelt gesendete Aenderungen nach Zeitueberschreitung erkennen (Idempotenz-Schluessel) #236
Labels
No labels
priority/could
priority/must
priority/should
priority/wont
status/blocked
status/claimed
status/done-migrated
type/bug
type/feature
type/infra
type/tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/todo#236
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Hintergrund
Seit #228 werden vormerkbare Aenderungen (Einkaufsliste/Vorrat) auch dann vorgemerkt und spaeter wiederholt, wenn der Server mit 504 antwortet oder die Anfrage nach 20 s abgebrochen wird. Das ist noetig, weil der Browser einen toten Server nicht von einem langsamen unterscheiden kann - sonst schlagen Aenderungen waehrend eines echten Ausfalls fehl.
Problem
Bei 504 oder Zeitueberschreitung kann der Server die Anfrage trotzdem verarbeitet haben. Die spaetere Wiederholung wird dann ein zweites Mal ausgefuehrt. Harmlos bei absoluten Aenderungen (umbenennen, verschieben, abhaken, Zielmenge setzen), aber doppelt gezaehlt bei:
CheckInPantryProductCommand,CheckOutPantryProductCommand,UndoPantryProductCheckOutCommand)CreatePantryProductCommand(doppelter Eintrag bzw. Menge doppelt beimergeWithExisting)AddOrActivateShoppingProductCommandBisher kein beobachteter Vorfall; Risiko aus dem Security-Review zu #228.
Akzeptanzkriterien
Abgrenzung
Claimed by session "Offene Issues [08a09a]"
Starte mit Design: Idempotenz-Schluessel pro vorgemerkter Aktion, Deduplizierung im Backend fuer die Delta-Befehle.
Design (Architect) + Security-Vorpruefung, Session "Offene Issues [08a09a]"
Client: Fuer jeden offline-faehigen Befehl erzeugt
callApivor dem ersten Versuch einen zufaelligen Schluessel (128 Bit,crypto.getRandomValues- funktioniert auch ohne HTTPS) und schickt ihn als HeaderIdempotency-Key. Wird die Aktion vorgemerkt, wird der Schluessel mit ihr gespeichert und bei jeder Wiederholung unveraendert gesendet. Bereits vorgemerkte Aktionen ohne Schluessel (vor dem Update) laufen wie bisher.Server: Neuer Decorator
IdempotentCommandDecoratorfuer die sechs Delta-Befehle (CheckIn/CheckOut/UndoCheckOut, CreatePantryProduct, AddOrActivateShoppingProduct - plus nichts sonst). Mit Schluessel und angemeldetem Benutzer:IDbTransaction) - der Eintrag existiert genau dann, wenn die Aenderung gespeichert ist (gleiches Prinzip wieRecipeIntegrationTransferEntity).Die Berechtigungspruefung laeuft vor dem Decorator - wer keinen Zugriff mehr hat, bekommt auch keine gespeicherte Antwort. Verschachtelte Aufrufe (laufende Transaktion) und Anfragen, deren Pfad nicht zum Befehl passt, ignorieren den Schluessel.
Daten: Tabelle
IdempotentRequests(UserId FK cascade, Key max. 100 Zeichen [A-Za-z0-9_-], RequestName, ResultJson, CreatedAtUtc). Eintraege aelter als 7 Tage werden beim naechsten Schreiben geloescht. Konto-Loeschung entfernt sie per Kaskade.Security: Schluessel pro Benutzer getrennt (ein fremder Schluessel trifft nie den Eintrag eines anderen). Gespeichert wird nur die Antwort, die der Benutzer ohnehin bekommen hat (eigene Listendaten), max. 7 Tage. Gleicher Schluessel fuer einen anderen Befehl -> 400 statt fremder Antwort. Ungueltiger Schluessel -> 400.
Umgesetzt in
46bad319(Backend),f49373d6(Frontend),1a493d89(Team-Memory). CI gruen (Lauf zub51cd2dd, der diese Commits enthaelt: Build, alle Tests, E2E, Docker).Scope
Idempotency-Key); wird er vorgemerkt, wird derselbe Schluessel bei jeder Wiederholung gesendet. Alte, schluessellose Eintraege laufen wie bisher.IdempotentCommandDecoratorfuer CheckIn/CheckOut/UndoCheckOut, CreatePantryProduct, AddOrActivateShoppingProduct. Befehl und Eintrag (UserId, Key, Ergebnis-JSON) in einer Transaktion; Wiederholung liefert das gespeicherte Ergebnis ohne erneute Ausfuehrung. Gleichzeitige Duplikate per Primaerschluessel aufgeloest. Berechtigungspruefung laeuft vorher; verschachtelte Aufrufe ignorieren den Schluessel. Schluessel fuer anderen Befehl -> 400, ungueltiger Schluessel -> 400. Aufbewahrung 7 Tage, Kaskade mit dem Benutzerkonto. Neue TabelleIdempotentRequestEntity(Migration AddIdempotentRequests).Tests
Security-Review: keine Findings. Hinweis (kein Sicherheitsproblem): ein gespeichertes Ergebnis wird nur am Befehlstyp, nicht an den Parametern erkannt - der Client erzeugt pro Aktion einen neuen Schluessel, daher ohne praktische Auswirkung.