Zum Inhalt springen

Aufgabe 08 - Der Wettlauf

Zu Zen-Modus wechseln

Sie bauen zwei Race Conditions absichtlich ein, reproduzieren sie zuverlässig und entschärfen sie: das Doppelklick-Desaster und die überholte Suchantwort. Beide Fehler existieren in realen Anwendung, und beide treten beim naiven Testen fast nie auf; genau das macht sie lehrreich (siehe Kapitel Nebenläufigkeit II).

  • Kapitel Nebenläufigkeit II, Abschnitt Race Conditions.
  • Node.js/TypeScript oder eine kleine HTML-Seite, VS Code.
  • Sie erkennen und reproduzieren Race Conditions gezielt.
  • Sie entschärfen sie mit In-Flight-Sperre, Abbruch oder Latest-wins.
  • Sie erklären, warum solche Fehler nur “manchmal” auftreten.
  • Reproduktion: den fehlerhaften Ablauf nach Anleitung nachbauen und beobachten (Teil A).
  • Reorganisation und Transfer: die Abwehrmuster anwenden und auf ein zweites Szenario übertragen (Teile B und C).
  • Reflexion, Problemlösung und Urteilsbildung: Auftretenswahrscheinlichkeit erklären, Muster auf reale Anwendungen übertragen und Abwehrstrategien vergleichen (Teil D).

Die Übung ist auf etwa zwei Stunden ausgelegt. Beide Szenarien bleiben umschaltbar kaputt und repariert im Code (Konstante FIXED), damit der Fehler vorführbar bleibt. Setzen Sie const FIXED = false um den Fehlerfall und auf const FIXED = true um den korrekten Fall zu zeigen. Teil D ist der Expertenteil.

  1. Simulieren Sie ein Konto mit einem Feld balance (Startwert 100) und einer Methode async withdraw(amount). Eine Abhebung läuft in drei Schritten: aktuellen Saldo lesen, sleep(100) als Verarbeitungszeit, neuen Saldo (balance - amount) schreiben. Es gilt die Regel: balance darf niemals negativ werden. Eine Abhebung, die mehr verlangt, als gedeckt ist (amount > balance), muss deshalb abgelehnt werden, statt den Saldo ins Minus zu schreiben. Genau diese Deckungsprüfung ist es, welche die Race Condition gleich aushebeln wird.
  2. Rufen Sie withdraw(80) zweimal ohne await dazwischen auf (der Doppelklick) und warten Sie dann auf beide. Notieren Sie: Was ist der End-Saldo, was hätte er sein müssen (eine Abhebung gebucht, die zweite abgelehnt, weil 160 nicht gedeckt sind)? Beobachten Sie, dass der Saldo trotz Deckungsprüfung negativ (-60) wird - beide Aufrufe lesen dieselben 100, bevor einer geschrieben hat, und halten die zweite Abhebung fälschlich für gedeckt.
  3. Erklären Sie den Ablauf schriftlich Zeile für Zeile: Wer liest wann welchen Saldo? Die Lücke zwischen Lesen und Schreiben ist der Kern.
  1. Beheben Sie das Problem mit einer der beiden Standardstrategien: In-Flight-Sperre (laufende Operation weist weitere ab) oder Warteschlange (Operationen laufen strikt nacheinander). Implementieren Sie eine, beschreiben Sie beide und begründen Sie Ihre Wahl in zwei Sätzen.
  2. Weisen Sie die Wirkung nach: Derselbe Doppelaufruf liefert jetzt das korrekte Ergebnis, und der abgewiesene beziehungsweise verzögerte Aufruf ist im Log sichtbar.
  1. Simulieren Sie search(term): braucht zufällig 100 bis 1500 Millisekunden und liefert eine Ergebnisliste, die den Suchbegriff enthält.
  2. Feuern Sie drei Suchen kurz hintereinander ab (“H”, “Ha”, “Hau”) und schreiben Sie jedes Ergebnis bei Ankunft in eine Variable displayed. Reproduzieren Sie den Fehler: Am Ende steht ein altes Ergebnis in displayed (bei Bedarf mit festen Laufzeiten nachhelfen: erste Suche am langsamsten).
  3. Beheben Sie es mit Latest-wins: Ein Anfragezähler sorgt dafür, dass nur die Antwort der jüngsten Anfrage übernommen wird. Weisen Sie nach, dass jetzt immer das Ergebnis zu “Hau” gewinnt.
  1. Lösen Sie die Suchbox ein zweites Mal, diesmal mit AbortController: Jede neue Suche bricht die vorige wirklich ab. Vergleichen Sie beide Lösungen in drei Sätzen (Ressourcen, Komplexität, wann welche?).
  2. Erklären Sie in drei Sätzen, warum diese Fehlerklasse beim Entwicklertest fast nie auftritt und im Betrieb doch (Stichworte: Timing, Last, langsame Verbindungen), und was das für das Testen bedeutet.
  3. Transfer: Nennen Sie zwei Stellen in Anwendungen, die Sie täglich benutzen, an denen genau diese Muster arbeiten müssen (je ein bis zwei Sätze), eine pro Szenario.
  1. Was genau ist die “Lücke”, in der die Doppelklick-Race-Condition entsteht?
  2. Worin unterscheiden sich In-Flight-Sperre und Warteschlange im Verhalten für den Benutzer?
  3. Warum reicht es nicht, den Button nur optisch zu deaktivieren?
  4. Wie funktioniert Latest-wins mit einem Anfragezähler?
  5. Was leistet AbortController gegenüber dem bloßen Ignorieren alter Antworten?

race.ts mit beiden Szenarien (umschaltbar kaputt/repariert), Logs beider Zustände und die schriftlichen Erklärungen als Kommentar.