Ύστερα από χρόνια συμπλήρωσης εντύπων και αναμονής, απέκτησες επιτέλους την τραπεζική σου άδεια. Αυτό σημαίνει ότι πλέον δικαιούσαι επίσημα να ανοίξεις τη δική σου τράπεζα, ζήτω!
Η πρώτη σου προτεραιότητα είναι να στήσεις και να θέσεις σε λειτουργία τα συστήματα πληροφορικής. Ύστερα από μια μέρα σκληρής δουλειάς, μπορείς ήδη να ανοίγεις και να κλείνεις λογαριασμούς, καθώς και να χειρίζεσαι αναλήψεις και καταθέσεις.
Επειδή βαριόσουν να γράψεις tests, καλείς κάποιους φίλους να σε βοηθήσουν να δοκιμάσετε το σύστημα. Ωστόσο, μόλις πέντε λεπτά αργότερα, ένας από τους φίλους σου ισχυρίζεται ότι έχασε χρήματα! Αν και είσαι σίγουρος ότι ο κώδικάς σου δεν έχει bugs, αρχίζεις να ψάχνεις τα αρχεία καταγραφής για να το ερευνήσεις.
Α, ναι, όπως ακριβώς υποψιαζόσουν, ο φίλος σου φταίει! Μοιράστηκε τα διαπιστευτήρια δοκιμής του με έναν άλλο φίλο, και μαζί συνωμότησαν να κάνουν καταθέσεις και αναλήψεις από τον ίδιο λογαριασμό παράλληλα. Ποιος θα έκανε κάτι τέτοιο;
Ενώ εσύ επιμένεις ότι είναι σωματικά αδύνατο να έχει κάποιος πρόσβαση στον λογαριασμό του παράλληλα, ο φίλος σου σε ενημερώνει με αυτάρεσκο ύφος ότι οι τραπεζικοί κανονισμοί απαιτούν να το υποστηρίζεις. Έτσι, χωρίς υποστήριξη παράλληλης τραπεζικής λειτουργίας, δεν δίνεται το σήμα για να ξεκινήσεις. Αναστενάζοντας, σημειώνεις νοερά να ασχοληθείς με αυτό αύριο. Αυτό θα καθυστερήσει την ημερομηνία κυκλοφορίας σου κατά τουλάχιστον μία ακόμη μέρα, αλλά τι να κάνουμε...
Ο στόχος σου είναι να υλοποιήσεις τραπεζικούς λογαριασμούς που υποστηρίζουν άνοιγμα/κλείσιμο, αναλήψεις και καταθέσεις χρημάτων.
Καθώς οι τραπεζικοί λογαριασμοί είναι προσβάσιμοι με πολλούς διαφορετικούς τρόπους (διαδίκτυο, κινητά τηλέφωνα, αυτόματες χρεώσεις), το τραπεζικό σου λογισμικό πρέπει να επιτρέπει την ασφαλή πρόσβαση στους λογαριασμούς από πολλά νήματα/διεργασίες (η ορολογία εξαρτάται από τη γλώσσα προγραμματισμού σου) παράλληλα. Για παράδειγμα, μπορεί να συμβαίνουν πολλές καταθέσεις και αναλήψεις παράλληλα· πρέπει να διασφαλίσεις ότι δεν υπάρχουν συνθήκες ανταγωνισμού ανάμεσα στη στιγμή που διαβάζεις το υπόλοιπο του λογαριασμού και στη στιγμή που ορίζεις το νέο υπόλοιπο.
Θα πρέπει να είναι δυνατό να κλείσεις έναν λογαριασμό· οι πράξεις σε έναν κλειστό λογαριασμό πρέπει να αποτυγχάνουν.
Στον κώδικα Roc, συνήθως εστιάζουμε στην επιχειρηματική λογική, ενώ η πλατφόρμα διαχειρίζεται τον ταυτοχρονισμό και την κοινόχρηστη κατάσταση.
Επομένως, σε αυτή την άσκηση χρειάζεται μόνο να υλοποιήσεις τις πράξεις του λογαριασμού στο BankAccount.roc, χωρίς να ανησυχείς για τον ταυτοχρονισμό.
Οι λογαριασμοί ξεκινούν κλειστοί, το άνοιγμα από την αρχή μηδενίζει το υπόλοιπο, και οι καταθέσεις και οι αναλήψεις πρέπει να είναι θετικές.
Το αρχείο bank-account-demo.roc που περιλαμβάνεται δείχνει πώς η υλοποίησή σου μπορεί να χρησιμοποιηθεί σε μια μικρή διαδικτυακή υπηρεσία.
Μπορείς να το διαβάσεις στον επεξεργαστή, αλλά είναι ανεξάρτητο από τα τεστ και δεν αποτελεί μέρος της λύσης σου.
Για να τρέξεις το demo, πρέπει να εγκαταστήσεις τη Roc στον υπολογιστή σου, να εγκαταστήσεις το CLI του Exercism και μετά να τρέξεις αυτή την εντολή σε ένα τερματικό:
exercism download --exercise=bank-account --track=roc
Έπειτα, υλοποίησε το BankAccount.roc και δοκίμασέ το με την ακόλουθη εντολή (άλλαξε την τοποθεσία λήψης που εκτυπώνει το CLI αν διαφέρει από τη διαδρομή παρακάτω):
cd ~/Exercism/roc/bank-account
roc test bank-account-test.roc
Όταν περάσουν όλα τα τεστ, μπορείς να ξεκινήσεις τον διακομιστή demo με την ακόλουθη εντολή:
roc bank-account-demo.roc
Ο διακομιστής χρησιμοποιεί την πλατφόρμα basic-webserver και την ενσωματωμένη υποστήριξη SQLite (η Roc κατεβάζει την πλατφόρμα την πρώτη φορά που εκτελείται).
Δεν χρειάζεται ξεχωριστός διακομιστής βάσης δεδομένων.
Ο διακομιστής ακούει στη διεύθυνση http://127.0.0.1:8000 και δημιουργεί το bank-account-demo.db στον τρέχοντα κατάλογό σου (όρισε το DB_PATH για να χρησιμοποιήσεις διαφορετικό αρχείο βάσης δεδομένων).
Σταμάτησε τον διακομιστή με Ctrl-C.
Τα δεδομένα διατηρούνται μετά από επανεκκινήσεις.
Σε άλλο τερματικό, μπορείς να καλέσεις την υπηρεσία χρησιμοποιώντας το curl (στα Windows, χρησιμοποίησε παντού το curl.exe για να αποφύγεις το alias curl του PowerShell).
Για παράδειγμα, ορίστε πώς ανοίγεις τον λογαριασμό #123, καταθέτεις 50, κάνεις ανάληψη 10 και ελέγχεις το υπόλοιπό του:
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
Το υπόλοιπο είναι 40.
Τα αναγνωριστικά λογαριασμών και τα ποσά πρέπει να είναι ακέραιοι χωρίς πρόσημο.
Οι ενημερώσεις επιστρέφουν OK σε περίπτωση επιτυχίας ή ένα σφάλμα από την υλοποίηση του λογαριασμού σου σε περίπτωση αποτυχίας.
Για να κλείσεις τον λογαριασμό #123:
curl -X POST http://127.0.0.1:8000/accounts/123/close
Για να δοκιμάσεις ταυτόχρονα αιτήματα σε Linux/macOS, χρησιμοποίησε ένα νέο αναγνωριστικό λογαριασμού και στείλε 100 καταθέσεις από οκτώ πελάτες:
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
Στα Windows, χρησιμοποίησε το 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
Όλες οι καταθέσεις θα πρέπει να πετύχουν και το υπόλοιπο θα πρέπει να είναι 100.
Κάθε αίτημα διαβάζει τον λογαριασμό, καλεί τις συναρτήσεις σου σε Roc και αποθηκεύει το αποτέλεσμα μέσα σε μια συναλλαγή Immediate της SQLite.
Οι αποτυχημένες πράξεις ακυρώνονται χωρίς να αλλάξουν τον λογαριασμό.
Η SQLite επιτρέπει έναν μόνο εγγραφέα κάθε φορά, εμποδίζοντας τα ταυτόχρονα αιτήματα να αντικαταστήσουν τις ενημερώσεις το ένα του άλλου (ακόμη και σε διαφορετικούς λογαριασμούς).
Σημείωση: Η άσκηση χρησιμοποιεί U64, αλλά το demo αποθηκεύει τα υπόλοιπα ως ακέραιους της SQLite και απορρίπτει ενημερώσεις που υπερβαίνουν τη μέγιστη τιμή I64 (9223372036854775807).
Για ένα παράδειγμα εκτέλεσης ανεξάρτητων υπολογισμών παράλληλα, δοκίμασε την άσκηση parallel-letter-frequency.
Χρησιμοποίησε το match για να διακρίνεις το Closed από το Open(balance).
Μια πράξη επιστρέφει έναν ενημερωμένο λογαριασμό αντί να τροποποιεί τον λογαριασμό που της περνιέται.
Χρησιμοποίησε το Ok(updated_account) για μια επιτυχημένη ενημέρωση και το Err(...) για μια πράξη που απορρίπτεται.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Roc με 135 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.