學習軌道
/
Roc
Roc
/
練習
/
銀行帳戶
銀行帳戶

銀行帳戶

簡單

簡介

經過多年填表和等待,你終於拿到銀行執照了。這代表你現在正式有資格開設屬於自己的銀行,太棒了!

你的第一要務,就是讓資訊系統順利運作起來。經過一整天的努力,你已經可以開立和關閉帳戶,也能處理提款和存款了。

因為你懶得寫測試,所以你邀請了幾位朋友來幫忙測試系統。然而,才過了五分鐘,其中一位朋友就說他們的錢不見了!雖然你對自己的程式碼很有信心,確信它沒有 bug,但你還是開始翻查日誌來調查。

啊,果然如你所料,是你的朋友有問題!他們把自己的測試憑證分享給另一位朋友,兩個人一起密謀,_同時_從同一個帳戶存款和提款。誰會做這種事啊?

當你主張一個人_不可能_同時存取自己的帳戶時,你的朋友得意地提醒你,銀行規則_要求_你必須支援這種情況。所以說,沒有支援並行銀行作業,就沒有上線的許可。你嘆了口氣,在心裡記下明天要處理這件事。這會讓你的上線日期_至少_再延後一天,不過,唉……

說明

你的任務是實作銀行帳戶,支援開戶/關閉帳戶、提款與存款。

由於銀行帳戶可以透過許多不同的方式存取(網路、手機、自動扣款),你的銀行軟體必須讓帳戶能被多個執行緒/行程(術語取決於你使用的程式語言)安全地平行存取。 例如,可能同時有許多筆存款與提款正在進行;你需要確保在讀取帳戶餘額與設定新餘額之間不會發生競爭條件。

帳戶應該要可以關閉;對已關閉帳戶進行的操作必須失敗。

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,以避免 PowerShell 的 curl 別名)。 舉例來說,以下示範如何開啟編號 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,透過 135 個練習 和真人引導來學習並精通 Roc,全部免費。