Треки
/
Roc
Roc
/
Вправи
/
Банківський рахунок
Банківський рахунок

Банківський рахунок

Легка

Вступ

Після років заповнення форм і очікування ми нарешті отримали банківську ліцензію. Це означає, що тепер ми офіційно маємо право відкрити власний банк, ура!

Наш перший пріоритет - запустити IT-системи й налагодити їхню роботу. Після дня наполегливої праці ми вже можемо відкривати і закривати рахунки, а також обробляти зняття коштів і поповнення рахунку.

Оскільки нам не хотілося писати тести, ми запрошуємо кількох друзів допомогти протестувати систему. Однак уже за пʼять хвилин один із наших друзів заявляє, що втратив гроші! Хоча ми впевнені, що наш код без помилок, ми починаємо гортати логи, щоб розібратися.

Атож, саме як ми й підозрювали, винен наш друг! Він поділився своїми тестовими обліковими даними з іншим другом, і разом вони змовилися робити депозити та знімати кошти з одного рахунку паралельно. Хто б таке зробив?

Поки ми доводимо, що фізично неможливо, щоб хтось отримав доступ до свого рахунку паралельно, наш друг самовдоволено повідомляє, що банківські правила вимагають від нас це підтримувати. Отже, без підтримки паралельної роботи з рахунками не буде й сигналу до запуску. Зітхнувши, ми подумки занотовуємо, що завтра над цим треба попрацювати. Це відсуне дату запуску ще щонайменше на один день, але що ж...

Вказівки

Реалізуйте банківські рахунки, які підтримують відкриття/закриття, зняття та внесення грошей.

Оскільки до банківських рахунків можна отримувати доступ багатьма різними способами (через інтернет, мобільні телефони, автоматичні списання), банківське програмне забезпечення має давати змогу безпечно отримувати доступ до рахунків з багатьох потоків/процесів (термінологія залежить від обраної мови програмування) паралельно. Наприклад, багато внесень і знять можуть відбуватися паралельно; забезпечте, щоб не було станів гонки між читанням балансу рахунку та встановленням нового балансу.

Має бути можливість закрити рахунок; операції із закритим рахунком мають завершуватися помилкою.

Обовʼязки Roc і хоста

У коді Roc ми зазвичай зосереджуємося на бізнес-логіці, а конкурентністю та спільним станом опікується платформа. Тому в цій вправі достатньо реалізувати операції з рахунком у BankAccount.roc, не турбуючись про конкурентність. Рахунки створюються закритими, повторне відкриття скидає баланс, а суми поповнень і зняттів мають бути додатними.

Необовʼязковий демо-сервер

Доданий файл bank-account-demo.roc показує, як нашу реалізацію можна використати в невеликому вебсервісі. Його можна прочитати в редакторі, але він не повʼязаний із тестами і не є частиною нашого рішення.

Щоб запустити демо-сервер, встановіть Roc на своїй машині, встановіть Exercism CLI, а потім виконайте цю команду в терміналі:

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

Далі реалізуйте BankAccount.roc і перевірте його такою командою (якщо шлях, який надрукував CLI, відрізняється від наведеного нижче, змініть місце завантаження):

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

Коли всі тести проходять, можна запустити демо-сервер такою командою:

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, щоб не натрапити на псевдонім 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 і зберігає результат у межах транзакції SQLite Immediate. Невдалі операції відкочуються, не змінюючи рахунок. SQLite дозволяє лише одного записувача водночас, тому конкурентні запити не перезаписують оновлення одне одного (навіть для різних рахунків).

Примітка: вправа використовує U64, але демо-сервер зберігає баланси як цілі числа SQLite і відхиляє оновлення, що перевищують максимальне значення I64 (9223372036854775807).

Приклад запуску незалежних обчислень паралельно є у вправі parallel-letter-frequency.

Підказки

Показати підказки

Використовуйте match, щоб розрізнити Closed і Open(balance). Операція повертає оновлений рахунок, а не змінює переданий їй рахунок. Використовуйте Ok(updated_account) для успішного оновлення та Err(...) для відхиленої операції.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Roc Exercism

Час розпочати Банківський рахунок?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Roc, а також 135 вправ та справжнє наставництво від людей, і все це безкоштовно.