0

Kỹ Sư Thực Chiến - Tư Duy Lường Trước Rủi Ro Hệ Thống Tập 5: "Lối Thoát Hiểm Khẩn Cấp" (Tư duy Rollback)

Với Fresher, deploy (triển khai) code lên Production thành công là đi ăn mừng. Nhưng với Senior, khoảnh khắc code vừa lên Production là lúc nhịp tim đập nhanh nhất. Bất chấp việc bạn đã test kỹ đến đâu ở môi trường Staging, Production luôn có những "đặc sản" mà bạn không lường trước được: Data quá to, cấu hình sai, hoặc đối tác bên thứ ba bóp băng thông.

Tư duy của người có kinh nghiệm là: Không bao giờ tin tưởng tuyệt đối vào bản release mới. Phải luôn tự hỏi: "Nếu luồng này sập, làm sao để lùi về bản cũ trong vòng 1 phút mà không cần viết lại code?"

Dưới đây là các kỹ thuật xây dựng "lối thoát hiểm" cho hệ thống.


1. Tách bạch giữa Deploy và Release (Feature Flags)

Một trong những sai lầm lớn nhất là gắn chặt việc "đẩy code lên server" (Deploy) đồng nghĩa với việc "khách hàng nhìn thấy tính năng mới" (Release). Khi code có lỗi, bạn phải hì hục git revert, chạy lại CI/CD pipeline, build lại Docker image, tốn ít nhất 10-15 phút. Trong 15 phút đó, công ty mất hàng trăm triệu và User thì gào thét.

  • Tư duy cốt lõi: Tính năng mới phải có "công tắc" (Toggle).
  • Chiến thuật Feature Flags (Cờ tính năng):
    • Bọc đoạn code xử lý luồng mới bằng một câu lệnh điều kiện (If/Else) đọc trạng thái từ Redis hoặc một hệ thống quản lý cờ (như Unleash).
    • Deploy code lên Production nhưng trạng thái cờ mặc định là OFF (luồng cũ vẫn chạy bình thường).
    • Khi team sẵn sàng, bật cờ sang ON. Nếu monitor lập tức báo lỗi đỏ lòm, không cần sửa code! Chỉ cần vào dashboard gạt cờ về lại OFF trong vỏn vẹn 1 giây. Hệ thống lùi về trạng thái an toàn ngay tức khắc.

2. Quy tắc Thép: Tương Thích Ngược (Backward Compatibility)

Khi hệ thống sập và bạn quyết định lùi version của Application (Backend) về bản cũ, một bi kịch khác thường xảy ra: Code bản cũ không còn đọc được Database bản mới.

  • Góc nhìn của Senior: Không bao giờ thay đổi cấu trúc bảng (Schema) hoặc API Contract một cách đột ngột phá vỡ luồng cũ.
  • Chiến thuật "Expand and Contract" (Mở rộng và Thu hẹp):
    • Giả sử bạn muốn đổi tên cột address thành shipping_address. Đừng viết lệnh RENAME COLUMN.
    • Phase 1 (Expand): Tạo thêm cột shipping_address. Code bản mới sẽ ghi dữ liệu vào cả hai cột, nhưng vẫn đọc từ cột cũ. Deploy lên. (Lúc này lùi code bản cũ vẫn chạy tốt).
    • Phase 2 (Migrate): Viết script copy data từ cột cũ sang cột mới. Đổi code sang Đọc và Ghi hoàn toàn ở cột mới.
    • Phase 3 (Contract): Sau vài tuần hệ thống chạy ổn định, không có nhu cầu Rollback nữa, lúc này mới tạo PR để Xóa cột address cũ đi.
  • Tương tự với API: Đừng xóa field hay đổi kiểu dữ liệu của API đang chạy. Hãy tạo phiên bản mới (/v2/payment) và giữ lại bản /v1/ cho đến khi tất cả các App Mobile/Client đã được update hết.

3. Thu hẹp Bán Kính Sát Thương (Canary Release)

Đừng bao giờ mở toang cửa đập. Nếu code có lỗi, hãy để lỗi đó chỉ ảnh hưởng đến một nhóm rất nhỏ, thay vì đánh sập toàn bộ User.

  • Tư duy cốt lõi: Đem con thỏ vào hầm mỏ để thử khí độc. Thỏ chết thì người không được xuống.
  • Chiến thuật ném đá dò đường (Canary):
    • Khi deploy version mới, chỉ cấu hình Load Balancer (Nginx/HAProxy) đẩy 5% traffic vào các server chạy bản mới. 95% traffic vẫn vào server bản cũ.
    • Kỹ sư ngồi nhìn chằm chằm vào biểu đồ Grafana và Kibana trong 30 phút. Nếu bản mới quăng lỗi 500 liên tục hoặc memory tăng đột biến -> Lập tức bẻ route Nginx, đưa 100% traffic về bản cũ.
    • "Bán kính sát thương" được giới hạn ở 5% user (thậm chí chỉ áp dụng cho user nội bộ công ty trước). Nếu ổn, mới tăng dần lên 20%, 50%, rồi 100%.

4. Rollback Dữ Liệu: Fix-Forward thay vì Revert

Code thì lùi (revert) được dễ dàng, nhưng Dữ liệu (State) một khi đã ghi xuống Database thì cực kỳ khó lùi. Đặc biệt với các hệ thống thanh toán hay soát vé (AFC). Nếu code lỗi đã lỡ trừ sai tiền của 1,000 khách hàng, việc bạn Rollback bản code cũ không làm tiền quay trở lại tài khoản của họ.

  • Tư duy cốt lõi: Database production chỉ được tiến lên, không được lùi lại. (Hạn chế tối đa việc chạy migrate down).
  • Chiến thuật Compensating Transaction (Giao dịch Bù trừ):
    • Thay vì cố gắng "xóa" bản ghi bị lỗi (rất nguy hiểm và dễ hỏng khóa ngoại FK), hãy thiết kế một luồng Fix-Forward.
    • Dựa vào bảng Audit Log (đã đề cập ở Tập 4), viết một script quét ra 1,000 giao dịch trừ sai tiền đó, và sinh ra 1,000 bản ghi mới mang tính chất "Hoàn tiền" (Refund/Reversal).
    • Mọi dấu vết sai lầm và sửa sai đều phải được lưu lại minh bạch trong hệ thống đối soát.

💡 Tổng Kết Series

Từ việc phòng thủ trước mạng chập chờn (Tập 1), chống nghẽn cổ chai (Tập 2), ngắt cầu dao để tránh chết chùm (Tập 3), truy vết dữ liệu (Tập 4) cho đến nghệ thuật thoát hiểm (Tập 5) - đó là hành trình lột xác để bước lên đẳng cấp của một System Engineer.

Không hệ thống nào hoàn hảo 100%, sự chuyên nghiệp của Kỹ sư được đánh giá bằng cách hệ thống đó sụp đổ một cách duyên dáng (Graceful degradation) và phục hồi một cách an toàn như thế nào.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí