Code chạy được trong demo là một chuyện; giữ cho hàng nghìn quy trình xuyên hệ thống lành mạnh trong production là chuyện khác. Bài cuối tập trung vào vận hành: nhìn thấy quy trình đang ở đâu, phát hiện đơn kẹt, và xử lý các tình huống thật sẽ xảy ra.
Correlation ID: sợi chỉ xuyên hệ thống
Mỗi quy trình sinh một correlation ID (thường là order_id hoặc một trace-id) và gắn nó vào mọi lời gọi tới mọi hệ thống, vào mọi dòng log. Khi có sự cố, bạn grep một ID và thấy toàn bộ hành trình qua WMS, accounting, payment.
[2026-07-10 10:22:01] order=ORD-8841 step=reserve status=ok wms_id=R-77
[2026-07-10 10:22:03] order=ORD-8841 step=invoice status=ok inv=INV-52
[2026-07-10 10:22:19] order=ORD-8841 step=charge status=503 attempt=1
[2026-07-10 10:22:22] order=ORD-8841 step=charge status=ok pay=PAY-19
Ba tín hiệu phải theo dõi
- Đơn kẹt (stuck):
statekhông phải trạng thái cuối vàupdated_atcũ hơn ngưỡng (ví dụ 15 phút). Đây là chỉ báo sức khỏe quan trọng nhất. - Tỷ lệ vào FAILED/COMPENSATED: tăng đột biến nghĩa là một hệ thống ngoài đang hỏng.
- Độ trễ mỗi bước và số retry trung bình: phát hiện đối tác chậm dần trước khi thành sự cố.
-- Bảng điều khiển "đơn kẹt"
SELECT order_id, state, attempt_count, updated_at
FROM order_workflow
WHERE state NOT IN ('PAID','COMPENSATED','FAILED')
AND updated_at < now() - interval '15 minutes'
ORDER BY updated_at;
Runbook: các tình huống thật
Tình huống A — Payment gateway sập 30 phút. Retry+backoff giữ đơn ở INVOICED và thử lại; circuit breaker giảm tải. Khi gateway hồi phục, các đơn tự tiếp tục. Việc của bạn: theo dõi hàng đợi, không hoảng hủy hàng loạt.
Tình huống B — Một đơn kẹt ở RESERVED 2 giờ. Kiểm log: bước invoice ném lỗi permanent 422 ("khách bị khóa công nợ"). Đây là quyết định nghiệp vụ: kích hoạt bù trừ (nhả hàng) và báo sales, không retry.
Tình huống C — Nghi ngờ double-charge. Tra correlation ID; nếu idempotency key được tôn trọng, gateway trả cùng payment_id → không trùng. Nếu thấy hai payment_id khác nhau cho một đơn, đó là bug key — vá và refund khoản thừa.
Tình huống D — Deploy phiên bản mới giữa lúc nhiều đơn đang chạy. Vì state bền và mỗi bước idempotent, worker mới đọc state cũ và tiếp tục an toàn — miễn là bạn không đổi ý nghĩa trạng thái cũ. Đổi schema state thì phải migrate tương thích ngược.
Khung tư duy vận hành: mỗi đơn luôn đang tiến hoặc đang được chú ý
Một orchestration khỏe mạnh có tính chất: không đơn nào im lặng thất bại. Mỗi đơn hoặc đang tiến tới trạng thái cuối, hoặc đã ở trạng thái cuối, hoặc đang nằm trong hàng chờ người xử lý có cảnh báo. Không có "vùng xám" mà đơn biến mất không ai biết.
Checklist sẵn sàng production
- [ ] Correlation ID trên mọi log và mọi lời gọi ngoài.
- [ ] Dashboard/truy vấn liệt kê đơn kẹt theo
updated_at. - [ ] Cảnh báo khi tỷ lệ FAILED vượt ngưỡng.
- [ ] Runbook cho: gateway sập, đơn kẹt, nghi double-charge.
- [ ] Công cụ cho ops: xem state, retry thủ công, ép bù trừ.
- [ ] Migration state tương thích ngược khi deploy.
- [ ] Test hồi phục: giết worker giữa chừng, xác nhận đơn chạy tiếp.
Các bước thiết lập vận hành
- Chuẩn hóa log có
order_idvà truyền ID xuống mọi adapter. - Dựng truy vấn/dashboard "đơn kẹt" chạy mỗi vài phút.
- Đặt alert theo tỷ lệ FAILED và tuổi đơn kẹt.
- Viết runbook ngắn cho 3–4 tình huống hàng đầu.
- Cung cấp nút ops: retry, ép compensate, đọc state — có ghi log ai thao tác.
Sai lầm thường gặp
- Không có correlation ID: sự cố xảy ra, bạn không lần được đơn qua các hệ thống.
- Không giám sát đơn kẹt: đơn treo âm thầm, khách phàn nàn mới biết.
- Ops chỉ có DB thô để sửa tay: dễ chuyển state sai, phá vỡ bất biến của state machine.
- Deploy đổi ý nghĩa trạng thái cũ: các đơn đang chạy diễn giải sai state, hỏng hàng loạt.
- Phản ứng thái quá khi đối tác sập: hủy hàng loạt thay vì để retry/backoff làm việc, gây thiệt hại không cần thiết.