This Rocket Ain't Stopping - Achieving Zero Downtime for Rails to Golang API Migration
Grab · 2016 · 2. Tăng trưởng · Độ khó: Trung bình — bài gốc
Bối cảnh
- Grab đang chuyển từ Rails + NodeJS sang kiến trúc service toàn Go. Một phần mục tiêu là về tổ chức: chuyển các kỹ sư Rails sang các team Go, chung một codebase.
- Một team API mới nhận nhiệm vụ chuyển hơn 30 endpoint public của app hành khách sang server Go, rồi trỏ DNS sang cluster Go.
Thay đổi chính
-
Quy tắc: mỗi endpoint phải qua unit test, load test, QA staging, shadow test trên production; deploy từng endpoint một, tăng dần, rollback được ngay.
-
Hướng đi bị bỏ: ban đầu muốn chiếm DNS trước bằng một proxy Go mỏng trước bản clone Rails, gọi qua gRPC. Gặp liên tiếp:
- ELB không hỗ trợ HTTP/2 nên phải cân bằng TCP, dẫn tới lệch tải khi auto-scale. Họ chữa bằng script restart Go dần.
- Gem gRPC của Ruby rò bộ nhớ (họ còn gửi PR sửa).
- Latency đo hai đầu không khớp.
Sau ~1 tháng họ bỏ hẳn hướng này và chuyển logic thật sang Go. Nhân tiện tách một số module thành service riêng cho các team khác nhau sở hữu.
-
Load test: phát lại log của ngày đông nhất trong tháng vào staging.
-
Shadow test: Rails xử lý như bình thường rồi gửi bản sao request + response sang Go; Go tự xử lý và so sánh bằng một JSON tokenizer tự viết. Lệch thì báo vào statsd và Slack.
- Với PUT/POST/DELETE: bọc DAO bằng mock, chỉ tính hàng DB dự kiến rồi so với hàng thật, để không ghi đôi.
- Lệch do trạng thái booking đổi giữa hai lần xử lý: hiếm, kiểm tra bằng tay.
-
Tăng shadow theo RPS, không theo %: 1% của một endpoint lớn đã hơn toàn bộ 5 endpoint khác cộng lại, từng làm ELB từ chối request. Họ cũng báo AWS "làm nóng" ELB trước.
-
Roll out từng endpoint theo RPS bằng cùng cơ chế, cuối cùng mới đổi DNS.
Vì sao
Theo Grab: một codebase Go chung cho toàn công ty; đổi DNS một lần cho 30+ endpoint đòi hỏi bản Go phải hoàn hảo ngay lập tức, điều không thực tế.
Bài học cho SWE
- Shadow traffic + so sánh tự động là cách kiểm chứng logic mới bằng traffic thật mà không ảnh hưởng user.
- Shadow cho lệnh ghi cần cơ chế riêng (mock DAO) để không ghi đôi.
- Phần trăm vô nghĩa nếu không biết tải tuyệt đối. Hãy điều khiển theo RPS.
- Dám bỏ một tháng công khi hướng đi sai.