✅ Checkpoint: Error handling và concurrency

By Everwise Intern

Quiz

  1. Trong một hàm chuyển tiền, vì sao dùng `expect("balance overflow")` sau khi đã trừ tiền ở tài khoản nguồn lại nguy hiểm hơn trả về `Result`?
    • Vì `Result` tự động rollback state khi trả `Err`.
    • Vì `expect` chậm hơn `Result` nên làm giảm throughput của service.
    • Vì panic làm chương trình dừng giữa giao dịch, tiền đã bị trừ mà không có bút toán đối ứng, và caller không thấy khả năng lỗi trong signature.
    • Vì `expect` chỉ chạy được trong debug build, release build sẽ bỏ qua kiểm tra.
  2. Với tiền lưu bằng `i64` cents, điều gì xảy ra với phép cộng bị overflow trong release build mặc định?
    • Chương trình panic ngay tại phép cộng.
    • Giá trị wrap âm thầm, sai mà không báo, nguy hiểm hơn panic.
    • Compiler tự chuyển sang `checked_add`.
    • Giá trị bị bão hòa ở `i64::MAX`.
  3. Lỗi `InsufficientFunds` nên được phân loại và xử lý thế nào?
    • Lỗi hệ thống: retry có backoff, trả 503, alert on-call.
    • Lỗi nghiệp vụ: trả 422, không retry, không alert on-call, log mức info/warn.
    • Lỗi nghiệp vụ nhưng vẫn retry vì có thể khách hàng nạp tiền vào sau.
    • Lỗi hệ thống nhưng trả 400 để tránh lộ chi tiết.
  4. Vì sao không được dùng `anyhow::Error` làm kiểu lỗi trong domain crate?
    • Vì `anyhow` chỉ dùng được trong test.
    • Vì `anyhow::Error` làm mất khả năng `match` từng variant, phá vỡ hợp đồng kiểu của domain.
    • Vì `anyhow` không hỗ trợ async.
    • Vì `anyhow::Error` không implement `Debug`.
  5. Một `async fn` trong Rust khi được gọi thì điều gì xảy ra?
    • Tokio tự động spawn nó lên một worker thread.
    • Nó chạy đồng bộ và trả về giá trị cuối cùng.
    • Thân hàm chạy ngay trên thread hiện tại cho tới `.await` đầu tiên.
    • Nó trả về một Future lazy, chưa chạy gì cho tới khi được `.await` hoặc spawn lên runtime.
  6. Khi chuyển tiền hai chiều giữa tài khoản 1 và 2 bằng nhiều task, cách nào phòng deadlock đúng?
    • Lock tài khoản nguồn trước rồi tới tài khoản đích, theo đúng chiều giao dịch.
    • Giữ lock lâu hơn để chắc chắn giao dịch hoàn tất trước task khác.
    • Dùng `try_lock` và bỏ qua giao dịch nếu không lấy được lock.
    • Luôn lock theo thứ tự account id tăng dần, bất kể chiều giao dịch.
  7. Vì sao giữ guard của `std::sync::Mutex` qua một `.await` là lỗi cần tránh?
    • Vì nó gây panic ngay khi guard bị drop.
    • Vì nó làm future mất `Send` và `tokio::spawn` từ chối biên dịch; nếu buộc phải giữ lock qua await thì dùng `tokio::sync::Mutex`.
    • Vì nó làm mất dữ liệu trong `HashMap` được bảo vệ.
    • Vì `std::sync::Mutex` không hỗ trợ đa luồng.
  8. Toán tử `?` desugar thành `match`; ở nhánh `Err`, nó gọi `From::from` để chuyển đổi kiểu lỗi. Vì vậy `?` chỉ biên dịch được khi đã có impl của trait ____ cho kiểu lỗi đích.
  9. Để nhiều task cùng sở hữu state, ta bọc state trong `Arc`. Để ghi an toàn khi đọc nhiều ghi ít, ta bọc thêm ____ (tên kiểu lock).
  10. Hãy tự đánh giá khả năng viết một luồng chuyển tiền async trả `Result`, không panic và không deadlock dưới tải đồng thời. Bạn đã làm được tới đâu?

Sign in to submit answers.