Tracks
/
Roc
Roc
/
Übungen
/
Bankkonto
Bankkonto

Bankkonto

Einfach

Einführung

Nach Jahren des Formularausfüllens und Wartens hast du endlich deine Banklizenz erworben. Das heißt, du bist jetzt offiziell berechtigt, deine eigene Bank zu eröffnen, hurra!

Deine erste Priorität ist es, die IT-Systeme ans Laufen zu bringen. Nach einem Tag harter Arbeit kannst du bereits Konten eröffnen und schließen sowie Abhebungen und Einzahlungen bearbeiten.

Da du keine Lust hattest, Tests zu schreiben, bittest du ein paar Freunde, dir beim Testen des Systems zu helfen. Doch schon nach fünf Minuten behauptet einer deiner Freunde, Geld verloren zu haben! Du bist dir zwar sicher, dass dein Code fehlerfrei ist, doch du fängst an, die Logs zu durchsuchen, um der Sache nachzugehen.

Ah ja, genau wie du vermutet hast, ist dein Freund schuld! Einer deiner Freunde hat seine Test-Zugangsdaten mit einem anderen Freund geteilt, und gemeinsam haben sie sich verschworen, vom selben Konto parallel Einzahlungen und Abhebungen vorzunehmen. Wer würde so etwas tun?

Während du argumentierst, es sei physisch unmöglich, dass jemand parallel auf sein Konto zugreift, teilt dein Freund dir selbstgefällig mit, dass die Bankregeln verlangen, dass du das unterstützt. Also: keine parallele Unterstützung fürs Banking, keine Freigabe für den Livegang. Seufzend machst du dir eine mentale Notiz, dich morgen darum zu kümmern. Das verschiebt deinen Starttermin um mindestens einen weiteren Tag, aber nun ja...

Anleitung

Deine Aufgabe ist es, Bankkonten zu implementieren, die das Eröffnen und Schließen sowie Abhebungen und Einzahlungen von Geld unterstützen.

Da auf Bankkonten auf viele verschiedene Arten zugegriffen werden kann (Internet, Mobiltelefone, automatische Abbuchungen), muss deine Banksoftware es ermöglichen, dass mehrere Threads/Prozesse (die Terminologie hängt von deiner Programmiersprache ab) parallel und sicher auf Konten zugreifen. Zum Beispiel kann es viele Einzahlungen und Abhebungen geben, die parallel ablaufen; du musst sicherstellen, dass zwischen dem Lesen des Kontostands und dem Setzen des neuen Kontostands keine Race Conditions auftreten.

Es sollte möglich sein, ein Konto zu schließen; Operationen auf einem geschlossenen Konto müssen fehlschlagen.

Zuständigkeiten von Roc und Host

Im Roc-Code konzentrieren wir uns normalerweise auf die Geschäftslogik, während die Plattform Nebenläufigkeit und gemeinsamen Zustand übernimmt. In dieser Übung musst du deshalb nur die Kontooperationen in BankAccount.roc implementieren und dich nicht um Nebenläufigkeit kümmern. Konten starten geschlossen, ein erneutes Öffnen setzt den Kontostand zurück, und Einzahlungen und Auszahlungen müssen positiv sein.

Optionale Server-Demo

Die mitgelieferte Datei bank-account-demo.roc zeigt, wie deine Implementierung in einem kleinen Webdienst verwendet werden kann. Du kannst sie im Editor lesen, aber sie ist unabhängig von den Tests und nicht Teil deiner Lösung.

Um die Demo auszuführen, musst du Roc installieren, die Exercism CLI installieren und dann diesen Befehl in einem Terminal ausführen:

exercism download --exercise=bank-account --track=roc

Als Nächstes implementierst du BankAccount.roc und testest es mit dem folgenden Befehl (ändere den Download-Pfad, den die CLI ausgibt, falls er von dem Pfad unten abweicht):

cd ~/Exercism/roc/bank-account
roc test bank-account-test.roc

Sobald alle Tests durchlaufen, kannst du den Demo-Server mit dem folgenden Befehl starten:

roc bank-account-demo.roc

Der Server nutzt die basic-webserver-Plattform und ihre eingebaute SQLite-Unterstützung (Roc lädt die Plattform beim ersten Start herunter). Ein separater Datenbankserver ist nicht nötig. Der Server lauscht auf http://127.0.0.1:8000 und legt bank-account-demo.db in deinem aktuellen Verzeichnis an (setze DB_PATH, um eine andere Datenbankdatei zu verwenden). Beende den Server mit Strg-C. Die Daten bleiben über Neustarts hinweg erhalten.

In einem anderen Terminal kannst du den Webdienst mit curl aufrufen (unter Windows verwendest du durchgängig curl.exe, um den curl-Alias von PowerShell zu vermeiden). So öffnest du zum Beispiel Konto Nr. 123, zahlst 50 ein, hebst 10 ab und prüfst den Kontostand:

curl -X POST http://127.0.0.1:8000/accounts/123/open
curl -X POST http://127.0.0.1:8000/accounts/123/deposit/50
curl -X POST http://127.0.0.1:8000/accounts/123/withdraw/10
curl http://127.0.0.1:8000/accounts/123/balance

Der Kontostand ist 40. Konto-IDs und Beträge müssen vorzeichenlose Ganzzahlen sein. Updates geben bei Erfolg OK zurück, bei einem Fehler den Fehler aus deiner Kontoimplementierung. So schließt du Konto Nr. 123:

curl -X POST http://127.0.0.1:8000/accounts/123/close

Um nebenläufige Anfragen unter Linux/macOS auszuprobieren, verwendest du eine neue Konto-ID und schickst 100 Einzahlungen von acht Clients:

curl -X POST http://127.0.0.1:8000/accounts/456/open
seq 100 | xargs -P 8 -I '{}' curl --fail --silent --show-error -X POST http://127.0.0.1:8000/accounts/456/deposit/1
curl http://127.0.0.1:8000/accounts/456/balance

Unter Windows verwendest du für das parallele Beispiel PowerShell 7+:

curl.exe -X POST http://127.0.0.1:8000/accounts/456/open
1..100 | ForEach-Object -Parallel { curl.exe --fail --silent --show-error -X POST http://127.0.0.1:8000/accounts/456/deposit/1 } -ThrottleLimit 8
curl.exe http://127.0.0.1:8000/accounts/456/balance

Alle Einzahlungen sollten erfolgreich sein und der Kontostand sollte 100 betragen.

Wie die Demo mit Nebenläufigkeit umgeht

Jede Anfrage liest das Konto, ruft deine Roc-Funktionen auf und speichert das Ergebnis innerhalb einer SQLite-Transaktion mit Immediate. Fehlgeschlagene Operationen werden zurückgerollt, ohne das Konto zu verändern. SQLite erlaubt nur einen Schreiber gleichzeitig, wodurch nebenläufige Anfragen die Updates der jeweils anderen nicht überschreiben können (auch nicht über verschiedene Konten hinweg).

Hinweis: Die Übung verwendet U64, die Demo speichert Kontostände aber als SQLite-Ganzzahlen und lehnt Updates ab, die den maximalen I64-Wert (9223372036854775807) überschreiten.

Ein Beispiel dafür, wie du unabhängige Berechnungen parallel ausführst, findest du in der Übung parallel-letter-frequency.

Hinweise

Hinweise anzeigen

Verwende match, um Closed von Open(balance) zu unterscheiden. Eine Operation gibt ein aktualisiertes Konto zurück, statt das übergebene Konto zu verändern. Verwende Ok(updated_account) für ein erfolgreiches Update und Err(...) für eine abgelehnte Operation.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Roc Exercism

Bereit, mit Bankkonto zu starten?

Melde dich bei Exercism an, um Roc mit 135 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.