Après des années à remplir des formulaires et à attendre, tu as enfin obtenu ta licence bancaire. Cela signifie que tu es désormais officiellement autorisé à ouvrir ta propre banque, hourra !
Ta première priorité est de mettre les systèmes informatiques en marche. Après une journée de dur labeur, tu peux déjà ouvrir et fermer des comptes, ainsi que gérer les retraits et les dépôts.
Comme tu n'avais pas envie de te fatiguer à écrire des tests, tu invites quelques amis à venir tester le système. Mais au bout de cinq minutes à peine, l'un de tes amis affirme avoir perdu de l'argent ! Même si tu es convaincu que ton code ne contient aucun bug, tu commences à parcourir les logs pour enquêter.
Ah oui, comme tu le soupçonnais, c'est ton ami qui est en tort ! Il a partagé ses identifiants de test avec un autre ami, et ensemble ils ont conspiré pour faire des dépôts et des retraits sur le même compte en parallèle. Qui ferait une chose pareille ?
Alors que tu soutiens qu'il est physiquement impossible d'accéder à son compte en parallèle, ton ami te fait remarquer d'un air suffisant que les règles bancaires exigent que tu prennes cela en charge. Donc, pas de prise en charge bancaire en parallèle, pas de feu vert pour la mise en production. Avec un soupir, tu prends mentalement note de t'y pencher demain. Cela repoussera ta date de lancement d'au moins un jour de plus, mais bon...
Ta tâche consiste à implémenter des comptes bancaires qui prennent en charge l'ouverture et la fermeture, les retraits et les dépôts d'argent.
Comme on peut accéder aux comptes bancaires de bien des manières (internet, téléphones mobiles, prélèvements automatiques), ton logiciel bancaire doit permettre d'accéder aux comptes en toute sécurité depuis plusieurs threads/processus (la terminologie dépend de ton langage de programmation) en parallèle. Par exemple, il peut y avoir de nombreux dépôts et retraits en parallèle ; tu dois t'assurer qu'il n'y a pas de conditions de concurrence entre le moment où tu lis le solde du compte et celui où tu définis le nouveau solde.
Il doit être possible de fermer un compte ; les opérations sur un compte fermé doivent échouer.
Dans le code Roc, on se concentre généralement sur la logique métier, tandis que la plateforme gère la concurrence et l'état partagé.
Par conséquent, dans cet exercice, il te suffit d'implémenter les opérations sur les comptes dans BankAccount.roc, sans avoir à te soucier de la concurrence.
Les comptes sont fermés au départ, une réouverture réinitialise le solde, et les montants des dépôts et des retraits doivent être positifs.
Le fichier bank-account-demo.roc fourni montre comment ton implémentation peut être utilisée dans un petit service web.
Tu peux le lire dans l'éditeur, mais il est indépendant des tests et ne fait pas partie de ta solution.
Pour lancer la démo, tu dois installer Roc sur ta machine, installer l'Exercism CLI, puis exécuter cette commande dans un terminal :
exercism download --exercise=bank-account --track=roc
Ensuite, implémente BankAccount.roc et teste-le avec la commande suivante (modifie l'emplacement de téléchargement affiché par la CLI s'il diffère du chemin ci-dessous) :
cd ~/Exercism/roc/bank-account
roc test bank-account-test.roc
Une fois que tous les tests passent, tu peux démarrer le serveur de démo avec la commande suivante :
roc bank-account-demo.roc
Le serveur utilise la plateforme basic-webserver et sa prise en charge intégrée de SQLite (Roc télécharge la plateforme au premier lancement).
Aucun serveur de base de données séparé n'est nécessaire.
Le serveur écoute sur http://127.0.0.1:8000 et crée bank-account-demo.db dans ton répertoire courant (définis DB_PATH pour utiliser un autre fichier de base de données).
Arrête le serveur avec Ctrl-C.
Les données sont conservées d'un redémarrage à l'autre.
Dans un autre terminal, tu peux appeler le service web avec curl (sous Windows, utilise curl.exe partout pour éviter l'alias curl de PowerShell).
Par exemple, voici comment ouvrir le compte n° 123, déposer 50, retirer 10 et vérifier son solde :
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
Le solde est de 40.
Les identifiants de compte et les montants doivent être des entiers non signés.
Les mises à jour renvoient OK en cas de succès ou une erreur de ton implémentation de compte en cas d'échec.
Pour fermer le compte n° 123 :
curl -X POST http://127.0.0.1:8000/accounts/123/close
Pour essayer des requêtes concurrentes sous Linux ou macOS, utilise un nouvel identifiant de compte et envoie 100 dépôts depuis huit 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
Sous Windows, utilise PowerShell 7+ pour l'exemple de parallélisme :
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
Tous les dépôts devraient réussir et le solde devrait être de 100.
Chaque requête lit le compte, appelle tes fonctions Roc et enregistre le résultat dans une transaction SQLite Immediate.
Les opérations qui échouent sont annulées sans modifier le compte.
SQLite n'autorise qu'un seul écrivain à la fois, ce qui empêche les requêtes concurrentes d'écraser mutuellement leurs mises à jour (même entre comptes différents).
Remarque : l'exercice utilise U64, mais la démo stocke les soldes sous forme d'entiers SQLite et rejette les mises à jour dépassant la valeur maximale de I64 (9223372036854775807).
Pour un exemple de calculs indépendants exécutés en parallèle, essaie l'exercice Fréquence des lettres en parallèle.
Utilise match pour distinguer Closed de Open(balance).
Une opération renvoie un compte mis à jour plutôt que de modifier le compte qui lui est passé.
Utilise Ok(updated_account) pour une mise à jour réussie et Err(...) pour une opération rejetée.
Inscris-toi sur Exercism pour apprendre et maîtriser Roc avec 135 exercices, et un vrai mentorat humain, le tout gratuitement.