Después de años de llenar formularios y esperar, por fin conseguiste tu licencia bancaria. Esto significa que ahora ya puedes abrir oficialmente tu propio banco, ¡hurra!
Tu primera prioridad es poner en marcha los sistemas informáticos. Después de un día de duro trabajo, ya puedes abrir y cerrar cuentas, además de manejar retiros y depósitos.
Como no tenías ganas de escribir pruebas, invitas a unos amigos para que te ayuden a probar el sistema. Sin embargo, apenas cinco minutos después, ¡uno de tus amigos dice que perdió dinero! Aunque confías en que tu código no tiene errores, empiezas a revisar los registros para investigar.
Ah, claro, tal como sospechabas, ¡la culpa es de tu amigo! Compartió sus credenciales de prueba con otro amigo y juntos conspiraron para hacer depósitos y retiros de la misma cuenta en paralelo. ¿A quién se le ocurriría hacer algo así?
Mientras argumentas que es físicamente imposible que alguien acceda a su cuenta en paralelo, tu amigo te informa con aire de superioridad que las reglas bancarias exigen que lo soportes. Así que, si no hay soporte para banca en paralelo, no hay luz verde para el lanzamiento. Suspiras y te haces una nota mental para trabajar en esto mañana. Esto retrasará tu fecha de lanzamiento al menos un día más, pero bueno...
Tu tarea es implementar cuentas bancarias que permitan abrir y cerrar cuentas, hacer retiros y depositar dinero.
Como se puede acceder a las cuentas bancarias de muchas maneras distintas (internet, teléfonos móviles, cargos automáticos), tu software bancario debe permitir que se acceda a las cuentas de forma segura desde varios hilos o procesos en paralelo (la terminología depende de tu lenguaje de programación). Por ejemplo, puede haber muchos depósitos y retiros ocurriendo en paralelo; debes asegurarte de que no haya condiciones de carrera entre el momento en que lees el saldo de la cuenta y el momento en que estableces el nuevo saldo.
Debe ser posible cerrar una cuenta; las operaciones sobre una cuenta cerrada deben fallar.
En el código de Roc, normalmente nos enfocamos en la lógica de negocio mientras que la plataforma se encarga de la concurrencia y del estado compartido.
Por eso, en este ejercicio solo necesitas implementar las operaciones de la cuenta en BankAccount.roc sin preocuparte por la concurrencia.
Las cuentas empiezan cerradas, volver a abrir reinicia el saldo, y los depósitos y los retiros deben ser positivos.
El archivo bank-account-demo.roc incluido muestra cómo se puede usar tu implementación en un pequeño servicio web.
Puedes leerlo en el editor, pero es independiente de las pruebas y no forma parte de tu solución.
Para ejecutar la demo, debes instalar Roc en tu computadora, instalar la CLI de Exercism y luego ejecutar este comando en una terminal:
exercism download --exercise=bank-account --track=roc
Después, implementa BankAccount.roc y pruébalo con el siguiente comando (cambia la ubicación de descarga que imprime la CLI si es distinta de la ruta de abajo):
cd ~/Exercism/roc/bank-account
roc test bank-account-test.roc
Cuando todas las pruebas pasen, puedes iniciar el servidor de la demo con el siguiente comando:
roc bank-account-demo.roc
El servidor usa la plataforma basic-webserver y su soporte integrado de SQLite (Roc descarga la plataforma la primera vez que se ejecuta).
No hace falta un servidor de base de datos aparte.
El servidor escucha en http://127.0.0.1:8000 y crea bank-account-demo.db en tu directorio actual (define DB_PATH para usar otro archivo de base de datos).
Detén el servidor con Ctrl-C.
Los datos sobreviven a los reinicios.
En otra terminal, puedes llamar al servicio web con curl (en Windows, usa curl.exe siempre para evitar el alias curl de PowerShell).
Por ejemplo, así puedes abrir la cuenta #123, depositar 50, retirar 10 y consultar su saldo:
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
El saldo es 40.
Los ID de cuenta y los montos deben ser enteros sin signo.
Las actualizaciones devuelven OK si todo sale bien o un error de tu implementación de la cuenta si falla.
Para cerrar la cuenta #123:
curl -X POST http://127.0.0.1:8000/accounts/123/close
Para probar solicitudes concurrentes en Linux/macOS, usa un ID de cuenta nuevo y envía 100 depósitos desde ocho clientes:
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
En Windows, usa PowerShell 7+ para el ejemplo en paralelo:
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
Todos los depósitos deberían completarse y el saldo debería ser 100.
Cada solicitud lee la cuenta, llama a tus funciones de Roc y guarda el resultado dentro de una transacción Immediate de SQLite.
Las operaciones que fallan se revierten sin cambiar la cuenta.
SQLite permite un solo escritor a la vez, lo que evita que las solicitudes concurrentes sobrescriban las actualizaciones de las demás (incluso entre cuentas distintas).
Nota: el ejercicio usa U64, pero la demo guarda los saldos como enteros de SQLite y rechaza las actualizaciones que superan el valor máximo de I64 (9223372036854775807).
Para ver un ejemplo de cómo ejecutar cálculos independientes en paralelo, prueba el ejercicio de frecuencia de letras en paralelo.
Usa match para distinguir Closed de Open(balance).
Una operación devuelve una cuenta actualizada en lugar de modificar la cuenta que recibe.
Usa Ok(updated_account) cuando la actualización salga bien y Err(...) cuando se rechace la operación.
Regístrate en Exercism para aprender y dominar Roc con 135 ejercicios y mentoría humana real, todo gratis.