پس از سالها پر کردن فرم و انتظار کشیدن، بالاخره مجوز بانکی خود را گرفتهاید. یعنی حالا رسماً میتوانید بانک خودتان را باز کنید، هورا!
اولین اولویتتان راهاندازی سیستمهای فناوری اطلاعات است. پس از یک روز کار سخت، هماکنون میتوانید حساب باز کنید و ببندید و همچنین برداشتها و واریزها را انجام دهید.
چون حوصلهی نوشتن Test را نداشتید، از چند دوست میخواهید که در آزمایش سیستم کمکتان کنند. اما فقط پنج دقیقه بعد، یکی از دوستانتان ادعا میکند که پول از دست داده است! هرچند مطمئنید که کدتان بدون Bug است، شروع میکنید لاگها را بررسی کنید تا بفهمید چه شده است.
آهان، دقیقاً همانطور که حدس میزدید، مقصر خود دوستتان است! او اطلاعات ورود آزمایشیاش را با دوست دیگری به اشتراک گذاشت و با هم توطئه کردند تا بهصورت موازی از همان حساب واریز و برداشت انجام دهند. چه کسی چنین کاری میکند؟
در حالی که شما بحث میکنید که از نظر فیزیکی غیرممکن است کسی بهصورت موازی به حساب خود دسترسی پیدا کند، دوستتان با خودپسندی به شما اطلاع میدهد که قوانین بانکی ملزمتان میکند که از این پشتیبانی کنید. بنابراین، بدون پشتیبانی از بانکداری موازی، سیگنالی برای راهاندازی در کار نیست. آهی میکشید و در ذهن خود یادداشت میکنید که فردا به این موضوع رسیدگی کنید. این کار تاریخ راهاندازی شما را حداقل یک روز دیگر عقب میاندازد، اما خب...
وظیفهی شما پیادهسازی حسابهای بانکیای است که از باز کردن و بستن، برداشت و واریز پول پشتیبانی میکنند.
از آنجا که میتوان از راههای گوناگون به حسابهای بانکی دسترسی داشت (اینترنت، تلفن همراه، کسرهای خودکار)، نرمافزار بانکی شما باید طوری باشد که بتوان بهطور موازی و ایمن از چند ریسمان/فرایند (اصطلاح آن بسته به زبان برنامهنویسی شما متفاوت است) به حسابها دسترسی داشت. برای نمونه، ممکن است واریزها و برداشتهای زیادی بهطور موازی رخ دهند؛ باید مطمئن شوید که میان خواندن موجودی حساب و تنظیم موجودی جدید، «شرایط رقابتی» رخ نمیدهد.
بستن حساب باید ممکن باشد؛ عملیاتی که روی حساب بسته انجام میشود باید با شکست مواجه شود.
در کد 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 فراخوانی کنید (در ویندوز همهجا از curl.exe استفاده کنید تا با مستعار curl در PowerShell قاطی نشود).
برای نمونه، اینطور حساب شمارهی ۱۲۳ را باز میکنید، ۵۰ واریز میکنید، ۱۰ برداشت میکنید و موجودی آن را بررسی میکنید:
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 برمیگردانند و در صورت شکست، خطایی از پیادهسازی حساب شما.
برای بستن حساب شمارهی ۱۲۳:
curl -X POST http://127.0.0.1:8000/accounts/123/close
برای آزمودن درخواستهای همزمان در لینوکس/macOS، یک شناسهی حساب تازه بردارید و ۱۰۰ واریز را از هشت کلاینت بفرستید:
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
در ویندوز برای نمونهی موازی از 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 استفاده میکند، اما دمو موجودیها را بهصورت عدد صحیح SQLite ذخیره میکند و بهروزرسانیهایی را که از بیشینهی مقدار I64 بزرگتر باشند رد میکند (9223372036854775807).
برای نمونهای از اجرای محاسبات مستقل بهصورت موازی، تمرین parallel-letter-frequency را امتحان کنید.
از match استفاده کنید تا Closed را از Open(balance) تشخیص دهید.
یک عملیات، حسابی بهروزشده برمیگرداند و حسابی را که به آن داده شده تغییر نمیدهد.
برای بهروزرسانی موفق از Ok(updated_account) و برای عملیات ردشده از Err(...) استفاده کنید.