M12 · Game day + mock interview
Mình làm xong M11 rồi. Giờ là bài thi cuối đúng không? Mình muốn AI tự gây sự cố mà không báo trước, rồi phỏng vấn mình như thật.
Đúng. M12 không có code mới. Bạn được kiểm tra hai việc: xử lý sự cố khi không biết trước và trình bày toàn bộ hệ thống trước người phỏng vấn.
1. Bối cảnh & bài toán
- Ở M5–M11, bạn biết trước mình sẽ kill cái gì. Production thì không báo trước.
- Dashboard, trace, alert từ M10 chỉ có giá trị nếu nhìn vào là đoán ra chỗ hỏng trong vài phút.
- Phỏng vấn senior thường hỏi: "Kể một sự cố bạn từng xử lý". Postmortem của M12 chính là câu chuyện đó, có số liệu thật.
2. Ôn lại trước khi bắt đầu
- Incident & postmortem: vai trò, mitigate trước root cause sau, blameless.
- SLI, SLO, alert: đo tác động bằng error budget.
- Partial failure & clock: clock skew, timeout không biết thành công hay thất bại.
- Timeout, retry, deadline và Circuit breaker, bulkhead.
- Connection pool: dấu hiệu cạn pool.
- Retry, DLX: poison message.
- CD & zero-downtime: rollback bad deploy.
- Khung trả lời phỏng vấn, Ticket booking, Flash sale.
- Kể chuyện theo STAR: kể lại sự cố.
3. Đề bài
Phần A: Game day. Chạy k6 tải nền 20 RPS liên tục. AI chọn ngẫu nhiên 3 thẻ trong 8 thẻ dưới đây, gây sự cố vào thời điểm không báo trước (hoặc đưa lệnh cho bạn nhờ người khác chạy mà bạn không xem). Bạn chỉ được nhìn alert, dashboard, trace, log, và phải tuyên bố incident trong 5 phút.
| # | Thẻ sự cố | Cách AI gây ra | Dấu hiệu bạn nên thấy | Mitigate mong đợi |
|---|---|---|---|---|
| 1 | PSP chậm | Toxiproxy latency 4s, jitter 2s cho payment → PSP | p99 confirm tăng, payment_psp_calls_total{outcome="timeout"} tăng, circuit breaker mở | CB fail fast, booking ở PENDING, reconciliation xử lý UNKNOWN |
| 2 | Kafka broker chết | docker stop kafka-N (hoặc 2 broker) | Under-replicated partitions, outbox pending tăng | Xác nhận ISR, khởi động lại broker, theo dõi outbox xả hết |
| 3 | Redis failover | docker stop redis-master | Lỗi kết nối Redis ngắn, rate limit tạm fail-open/fail-closed | Xác nhận master mới, kiểm tra không trùng ghế |
| 4 | Cạn connection DB | Script mở 100 transaction pg_sleep giữ connection, hoặc hạ max_connections | Hikari pending tăng, connection is not available timeout | Tìm và kill session giữ connection, giảm tải, xem lại pool size |
| 5 | Poison message | Publish 1 message JSON hỏng vào payment.events / queue notification | Consumer lỗi lặp, lag tăng, DLQ có message | Message vào DLQ/skip, consumer tiến lên, sửa và replay |
| 6 | Clock skew | libfaketime lệch +10 phút trên 1 instance booking | Hold hết hạn sớm/bất thường, JWT nbf/exp lỗi 401 | Xác định instance lệch, gỡ khỏi traffic; thời hạn hold tính theo giờ DB |
| 7 | Bad deploy | Deploy image có bug (vd confirm luôn 500 với 10% request) qua Helm | Error rate tăng ngay sau deploy, trùng thời điểm rollout | helm rollback trước, tìm bug sau |
| 8 | Traffic spike | k6 tăng từ 20 lên 500 RPS trong 30s | Rate limit 429, HPA scale, p99 tăng | Hệ thống từ chối có kiểm soát, không bán trùng, không sập |
Với mỗi thẻ bạn phải có sẵn runbook trong docs/runbooks/ theo khung:
# Runbook: <tên>
Triệu chứng / alert nào bắn:
Dashboard / query cần mở:
Kiểm tra nhanh (lệnh):
Mitigate (theo thứ tự, an toàn trước):
Xác nhận đã hồi phục:
Khi nào escalate:
Phần B: Postmortem cho mỗi sự cố, docs/postmortems/YYYY-MM-DD-<slug>.md:
# Postmortem: <tiêu đề>
Trạng thái: Draft | Reviewed Mức độ: SEV1/2/3
## Tóm tắt (3 câu)
## Tác động: bao nhiêu request lỗi, bao nhiêu booking ảnh hưởng, error budget tiêu hao
## Timeline (UTC): phát hiện → tuyên bố → mitigate → hồi phục
## Phát hiện bằng gì (alert/dashboard), mất bao lâu (TTD)
## Root cause (5 Whys)
## Điều gì tốt / điều gì chưa tốt / may mắn ở đâu
## Action items: | việc | loại (prevent/detect/mitigate) | owner | hạn |
## Bằng chứng: ảnh dashboard, traceId, lệnh đã chạy
Phần C: Mock interview 45 phút: "Thiết kế hệ thống đặt vé sự kiện". AI đóng vai interviewer, bạn không mở code.
| Phút | Nội dung |
|---|---|
| 0–5 | Làm rõ yêu cầu, số liệu (người dùng, đỉnh RPS, số ghế/event), SLO |
| 5–10 | Capacity estimate, API chính |
| 10–20 | Kiến trúc tổng thể, data model (events, seats, bookings, payments, outbox, inbox) |
| 20–35 | Deep dive do interviewer chọn: chống bán trùng, giữ ghế hết hạn, payment UNKNOWN, saga, flash sale |
| 35–42 | Sự cố, HA, observability, "tải x10 thì sao?", multi-region |
| 42–45 | Trade-off, điều sẽ làm khác, câu hỏi ngược |
Rubric (mỗi mục 1–4 điểm, đạt khi ≥ 24/32 và không mục nào 1):
| Tiêu chí | 4 điểm trông như thế nào |
|---|---|
| Làm rõ yêu cầu | Hỏi đúng câu, ra số liệu và SLO trước khi vẽ |
| Ước lượng | Từ số liệu ra RPS, storage, số instance, có làm tròn hợp lý |
| Kiến trúc | Ranh giới service rõ, giải thích vì sao tách |
| Dữ liệu & tính đúng | Chống bán trùng bằng DB, idempotency, outbox/inbox, state machine |
| Chịu lỗi | Timeout, retry, CB, saga bù trừ, payment UNKNOWN |
| Scale & HA | Biết điểm nghẽn, số liệu benchmark thật từ M9/M11 |
| Vận hành | SLO, alert, trace, kể được sự cố từ game day |
| Giao tiếp & trade-off | Nói được "chọn A vì…, chấp nhận mất B", trả lời bằng tiếng Anh được |
4. Acceptance criteria
- Có 8 runbook, mỗi thẻ một bản, theo đúng khung.
- Với 3 sự cố AI gây: tuyên bố incident ≤ 5 phút (TTD), mitigate ≤ 15 phút (TTM).
- Sau mọi sự cố: 0 ghế bán trùng, 0 payment bị trừ tiền mà booking không CONFIRMED (đối soát bằng SQL).
- Mỗi sự cố có postmortem đủ mục, có ít nhất 2 action item, có số liệu tác động.
- Ít nhất 1 action item được làm ngay (vd thêm alert còn thiếu) và có commit.
- Mock interview đạt ≥ 24/32 theo rubric, không mục nào 1 điểm.
- Kể được 1 sự cố theo STAR bằng tiếng Anh trong 2 phút.
5. Test cases
| ID | Kịch bản | Điều kiện | Kỳ vọng |
|---|---|---|---|
| G1 | Đối soát sau game day | Kết thúc 3 sự cố | Query bán trùng và query payment lệch đều 0 dòng |
| G2 | Alert đúng | Mỗi thẻ | Có ít nhất 1 alert bắn hoặc ghi vào postmortem là "phát hiện thủ công" + action item |
| G3 | Runbook dùng được | Người khác (hoặc AI) làm theo runbook | Mitigate được không cần hỏi tác giả |
| G4 | Rollback | Thẻ 7 | Error rate về baseline ≤ 5 phút sau rollback |
| G5 | Poison message | Thẻ 5 | Consumer lag về 0, message lỗi nằm trong DLQ |
-- reconciliation.sql: chạy sau mỗi sự cố
-- 1. ghế bán trùng
SELECT seat_id, count(*) FROM bookings WHERE status='CONFIRMED' GROUP BY seat_id HAVING count(*)>1;
-- 2. payment thành công nhưng booking không CONFIRMED
-- TODO: viết query join payments ↔ bookings (khác DB thì export rồi so, hoặc gọi API)
-- 3. hold đã hết hạn mà ghế vẫn HELD
-- TODO
6. Failure scenarios
Chính là 8 thẻ ở mục 3. Thêm luật chơi:
| Biến thể | Cách tạo | Mong đợi |
|---|---|---|
| Hai sự cố chồng nhau | Thẻ 1 + thẻ 8 cùng lúc | Ưu tiên đúng: ổn định tải trước, không đổi nhiều thứ cùng lúc |
| Alert im lặng | AI tắt 1 alert rule trước khi gây sự cố | Bạn vẫn phát hiện từ dashboard/SLO, ghi action item cho detection |
| Mitigate sai | Bạn restart nhầm service | Ghi thật vào timeline, blameless |
7. Số liệu cần thu
docs/benchmarks/m12-gameday.md: bảng mỗi sự cố với TTD, TTM, số request lỗi, error budget tiêu hao, có alert hay không. Điểm rubric mock interview và nhận xét của AI lưu ở docs/interview/mock-1.md.
8. Checklist hoàn thành (M12)
- ☐ 8 runbook, 3 postmortem, file số liệu game day.
- ☐ Reconciliation SQL chạy sạch.
- ☐ Action item đã làm có commit.
- ☐ Mock interview đạt rubric.
- ☐ ADR: "Fail-open hay fail-closed khi Redis rate limiter chết".
- ☐ Không hardcode secret trong script gây sự cố.
- ☐ Đã qua lượt review và phỏng vấn của AI.
Checklist cuối cho toàn project
- ☐ M0–M12 đều qua Definition of Done.
- ☐
docker compose up(hoặchelm install) dựng toàn bộ hệ thống từ repo sạch bằng 1 lệnh, README hướng dẫn ≤ 10 bước. - ☐ Postman collection chạy hết luồng: login → xem event → hold → confirm → nhận notification.
- ☐ CI xanh; test unit, integration (Testcontainers), concurrency, contract.
- ☐ Chống bán trùng chứng minh bằng test song song và k6.
- ☐ Payment idempotent, xử lý UNKNOWN bằng reconciliation.
- ☐ Saga + outbox/inbox: không mất, không xử lý trùng event.
- ☐ Benchmark M9 và số liệu HA M11 có trong
docs/benchmarks/. - ☐ Security: 401/403/BOLA có test, không secret trong git.
- ☐ Trace end-to-end, dashboard có SLO, alert theo burn rate.
- ☐ ADR cho mọi quyết định lớn (≥ 8 ADR).
- ☐ Sơ đồ kiến trúc cập nhật đúng với code.
- ☐ Một trang "project story" để đi phỏng vấn (xem Tự giới thiệu & kể về project).
Lúc AI gây sự cố, mình thấy error rate tăng nhưng không biết bắt đầu từ đâu, mở lung tung mất 10 phút.
Không đưa đáp án, chỉ đưa thứ tự câu hỏi để bạn tự đi:
- Lỗi bắt đầu lúc nào? Có trùng với deploy, thay đổi config, tăng tải không?
- Lỗi ở endpoint nào, service nào? Tất cả instance hay chỉ 1 instance (gợi ý clock skew, bad pod)?
- Latency tăng hay lỗi tăng? Latency tăng trước thường là phụ thuộc chậm (PSP, DB pool).
- Mở 1 trace lỗi: span nào dài nhất hoặc đỏ đầu tiên?
- Lag consumer, outbox pending, Hikari pending: cái nào bất thường?
Hãy biến danh sách này thành panel "First 5 minutes" ở đầu dashboard. Đó là một action item tốt.
Root cause của mình ghi là "mình quên cấu hình DLQ cho queue". Vậy có được không?
Chưa đủ. Blameless nghĩa là hỏi tiếp: vì sao hệ thống cho phép quên? Gợi ý: có test nào bắt được queue thiếu DLQ không? Cấu hình queue có được khai báo bằng code/review không? Alert nào lẽ ra phải báo lag tăng? Action item tốt là thứ làm cho lần sau không thể quên, không phải "lần sau cẩn thận hơn".
Mình tập thử thì 20 phút đầu đã đi vào chi tiết @Version với SQL, chưa kịp vẽ kiến trúc tổng thể.
Gợi ý: luyện "đi rộng trước, sâu sau". Mỗi khối trong sơ đồ nói 1 câu, rồi hỏi interviewer "Anh muốn em đi sâu vào phần nào?". Chuẩn bị sẵn 3 deep dive mỗi cái 5 phút (chống bán trùng, payment UNKNOWN, flash sale), mỗi cái kèm 1 con số thật từ project. Bấm giờ khi tập.
Mình làm xong rồi, review và phỏng vấn mình đi.
Checklist review AI sẽ soi
- Timeline postmortem có giờ chính xác, có TTD/TTM, có bằng chứng (traceId, ảnh).
- Root cause dừng ở hệ thống/quy trình, không dừng ở con người.
- Action item có owner, hạn, loại; không phải "cẩn thận hơn".
- Runbook ghi lệnh chạy được, không ghi chung chung.
- Mitigate có ưu tiên an toàn (rollback, giảm tải) trước khi sửa root cause.
- Reconciliation SQL thật sự chạy và kết quả được ghi lại.
Câu hỏi phỏng vấn
1. Kể một sự cố bạn đã xử lý.
🇻🇳 Theo STAR: tình huống (PSP chậm 4s trong lúc tải), việc của mình (on-call), hành động (thấy p99 tăng, trace chỉ span PSP, CB mở, booking chờ, reconciliation xử lý UNKNOWN), kết quả (0 trừ tiền sai, X request lỗi, thêm alert cho timeout PSP).
🇬🇧 During a game day the payment provider slowed to four seconds. Traces pointed to the PSP span, the circuit breaker opened, bookings stayed pending and reconciliation resolved unknown payments. No customer was double charged, and we added an alert on PSP timeouts.
2. Khi có sự cố, bạn mitigate trước hay tìm root cause trước?
🇻🇳 Mitigate trước: rollback, giảm tải, gỡ instance lỗi. Root cause tìm sau khi đã ổn định, bằng postmortem.
🇬🇧 Mitigate first: roll back, shed load or remove the bad instance. Root cause analysis comes after the system is stable, in the postmortem.
3. Blameless postmortem là gì, vì sao cần?
🇻🇳 Tập trung vào hệ thống và quy trình đã cho phép lỗi xảy ra, không đổ lỗi cá nhân. Nhờ vậy mọi người khai thật và action item sửa được tận gốc.
🇬🇧 It focuses on how the system allowed the failure rather than who caused it, so people share honestly and the action items fix the system.
4. Nếu được làm lại Ticket Booking, bạn đổi gì?
🇻🇳 Trả lời bằng trade-off có số liệu: ví dụ bỏ Redis lock vì DB constraint đã đủ ở tải đo được, hoặc dùng CDC thay outbox polling khi lag cao. Luôn kèm điều kiện "nếu tải x10 thì...".
🇬🇧 Answer with measured trade-offs, for example dropping the Redis lock because the database constraint was enough at our measured load, and say what would change at ten times the traffic.
Cách nhớ ngắn gọn
Game day: tải nền → sự cố bất ngờ → tuyên bố ≤5' → mitigate ≤15' → đối soát
Thứ tự nhìn: khi nào · ở đâu · latency hay lỗi · trace · lag/pool
Postmortem: tác động · timeline · 5 Whys · action item có owner
Phỏng vấn 45': yêu cầu → ước lượng → kiến trúc → deep dive → vận hành → trade-off
Tự kiểm tra
TTD và TTM là gì, đo bằng cách nào trong game day?
Time to detect và time to mitigate. Lấy từ timeline: lúc AI gây sự cố, lúc bạn tuyên bố incident, lúc error rate về baseline.
Poison message ảnh hưởng gì nếu không có DLQ?
Consumer lỗi lặp vô hạn trên cùng message, chặn cả partition/queue, lag tăng mãi.
Vì sao thời hạn giữ ghế nên tính theo giờ DB chứ không theo giờ của từng instance?
Clock các máy có thể lệch; dùng một nguồn thời gian chung giúp mọi instance đánh giá hết hạn giống nhau.
Bad deploy: rollback hay fix forward?
Mặc định rollback để dừng thiệt hại nhanh, fix forward chỉ khi rollback không an toàn (vd migration không lùi được).
Action item tốt khác action item xấu thế nào?
Tốt: cụ thể, có owner, có hạn, thay đổi hệ thống (test, alert, guardrail). Xấu: "cẩn thận hơn".