Zum Inhalt springen

Aufgabe 04-2a - Der Wettlauf

Zu Zen-Modus wechseln
  • Du erkennst Race Conditions und reproduzierst sie gezielt.
  • Du sicherst sie mit In-Flight-Sperre, Abbruch oder latest wins ab.
  • Du erklärst, warum solche Fehler nur “manchmal” auftreten, und vergleichst die Abwehrstrategien.

Ein Gast, der zweimal bestellt, weil das Essen nicht kommt, ist im Lokal ärgerlich. Bei einem Bankkonto ist er ein Problem: Zwei Abhebungen, die beide dieselben 100 Euro sehen, ziehen gemeinsam 160 Euro ab.

Du baust zwei Race Conditions absichtlich ein, machst sie zuverlässig sichtbar und entschärfst sie: das Doppelklick-Desaster und die überholte Suchantwort (siehe Kapitel Nebenläufigkeit II: Koordination, Einheit 4.2). Beide Fehler gibt es in echten Anwendungen, und beide treten beim naiven Testen fast nie auf. Alles landet in race.ts. Beide Szenarien bleiben umschaltbar kaputt und repariert: const FIXED = false zeigt den Fehler, const FIXED = true die Abwehr.

Projektordner anlegen siehe https://qiz.at/projektordner

  • Lies vorher kurz https://qiz.at/assignmentsabgabe durch.
  • Nur vollständige Abgaben (siehe https://qiz.at/rules) können positiv bewertet werden.
  • Abgabe- und Testdetails siehe https://qiz.at/assignmentsabgabe
  • Die Übung liegt als Unterordner uebung-04-2a in deinem Jahres-Repository SJ26_27_4AHITM_SEW_<Benutzername>_exercises, eingerichtet wie in Aufgabe 03-2a. Die Logs beider Zustände (FIXED auf false und auf true) und die schriftlichen Erklärungen stehen als Kommentar in race.ts. Du arbeitest allein.
  • Simulier ein Konto mit balance (Startwert 100) und einer Methode async withdraw(amount). Eine Abhebung läuft in drei Schritten: Saldo lesen und prüfen, ob er reicht (sonst ablehnen), sleep(100) als Verarbeitungszeit, dann den Betrag abziehen. Es gilt: balance darf niemals negativ werden.
  • Ruf withdraw(80) zweimal ohne await dazwischen auf (der Doppelklick) und warte dann auf beide. Notiere den Endsaldo und was er hätte sein müssen.
  • Erklär schriftlich Zeile für Zeile: Wer liest wann welchen Saldo? Warum hilft die Deckungsprüfung nicht?
  • Behebe das Problem mit einem In-Flight-Flag: Solange eine Abhebung läuft, wird eine weitere abgewiesen. Weise nach, dass derselbe Doppelaufruf jetzt das richtige Ergebnis liefert und der abgewiesene Aufruf im Log sichtbar ist.

Tipp: Der Kern ist die Lücke zwischen Lesen und Schreiben. Setz das Flag vor die Lücke und gib es in einem finally wieder frei, sonst bleibt das Konto nach einem Fehler gesperrt.

Vollständig, wenn:

  • der Fehler mit FIXED = false reproduzierbar ist (negativer Endsaldo) und der Soll-Wert notiert ist.
  • die Erklärung zeigt, dass beide Aufrufe denselben Saldo lesen, bevor einer schreibt.
  • mit FIXED = true genau eine Abhebung gelingt und die zweite abgewiesen wird.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Simulier search(term): braucht 100 bis 1500 Millisekunden und liefert eine Ergebnisliste mit dem Suchbegriff.
  • Feuer drei Suchen kurz hintereinander ab (“H”, “Ha”, “Hau”) und schreib jedes Ergebnis bei Ankunft in eine Variable displayed. Reproduzier den Fehler: Am Ende steht ein altes Ergebnis in displayed.
  • Behebe es mit latest wins: Ein Anfragezähler sorgt dafür, dass nur die Antwort der jüngsten Anfrage übernommen wird. Weise nach, dass jetzt immer das Ergebnis zu “Hau” gewinnt.

Tipp: Zufällige Laufzeiten machen den Fehler unzuverlässig. Für einen sicheren Nachweis gib die Laufzeiten fest vor, die erste Suche am langsamsten.

Vollständig, wenn:

  • der Fehler mit FIXED = false zuverlässig reproduziert ist.
  • mit FIXED = true immer das Ergebnis zu “Hau” angezeigt wird und verworfene Antworten im Log stehen.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Löse die Suchbox ein zweites Mal mit AbortController: Jede neue Suche bricht die vorige wirklich ab. Deine simulierte Suche muss dafür ein signal annehmen und bei abort ihren Timer beenden.
  • Vergleich beide Lösungen in drei Sätzen: Ressourcen, Aufwand, wann welche?
  • Erklär in drei Sätzen, warum diese Fehlerklasse beim Entwicklertest fast nie auftritt und im Betrieb doch, und was das für das Testen bedeutet.

Tipp: Ein abgebrochener Vorgang scheitert mit einem Fehler, dessen name "AbortError" ist. Diesen Fall fängst du ab, alle anderen Fehler wirfst du weiter.

Vollständig, wenn:

  • nur die letzte Suche fertig wird und die abgebrochenen im Log stehen.
  • der Vergleich Ressourcen und Aufwand stichhaltig gegenüberstellt.
  • die Erklärung Timing, Last und langsame Verbindungen nennt und eine Folgerung für das Testen zieht.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieser Datei (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Bau die zweite Abwehrstrategie für das Konto: eine Warteschlange, in der jede Abhebung erst startet, wenn die vorige fertig ist. Teste sie mit drei Aufrufen (30, 30, 80).
  • Beurteile, welche der beiden Strategien (Sperre oder Warteschlange) für einen Doppelklick passt und welche für zwei gewollte Abhebungen, und begründe es mit dem Verhalten für den Benutzer.
  • Nenn zwei Stellen in Apps, die du täglich benutzt, an denen eines der Muster dieser Aufgabe arbeiten muss, und ordne jeder das passende Muster zu.

Tipp: Eine Warteschlange lässt sich mit einer Promise-Kette bauen: Jede neue Abhebung hängt sich mit then an das Promise der vorigen.

Vollständig, wenn:

  • die Warteschlange alle Abhebungen nacheinander ausführt und die nicht gedeckte ablehnt.
  • die Beurteilung Sperre und Warteschlange dem passenden Fall zuordnet und begründet.
  • beide Beispiele aus dem Alltag einem Muster richtig zugeordnet sind.

Wie unter Allgemein.

  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?

Durchschnittlich bis Wesentlich_vollständig: ca. 100 min

  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules