Tuần này Cloudflare ra mắt Worker Previews: chạy npx wrangler preview trên một branch là có ngay một môi trường preview đầy đủ — code riêng, config riêng, URL riêng, Durable Object namespace riêng, Container riêng. Push tiếp thì tự deploy lại, nếu Worker của bạn đang dùng Git-connected Workers Builds. Nghe thì đúng là thứ mà team nào làm trên Workers cũng từng tự chế bằng tay, bằng mấy cái tên environment có hậu tố và một ít may mắn.

Mình đi tìm cái phần không được ghi trong changelog, và tìm thấy nó nằm gọn trong một câu ở docs: một service binding từ Preview vẫn gọi tới bản deploy production của Worker được bind tới. Không phải bản preview của Worker kia. Production. Lần nào cũng vậy.

Cái gì thực sự được cô lập

Chạy lệnh, đọc kỹ xem mình nhận được gì:

npx wrangler preview

Từ một feature branch, lệnh này tạo một preview riêng. Cloudflare mô tả: “một nơi giống production để chạy, với code, config, URL, observability và state của riêng nó.” Phần cô lập là thật, và đúng cho những thứ quan trọng nhất khi test hàng ngày:

  • Durable Objects — namespace mới toanh cho từng preview, tự tạo ngay lần đầu chạy lệnh trên branch đó
  • Containers — tương tự, một Container application mới cho từng branch
  • State và storage — giới hạn trong branch đó, nên hai kỹ sư test hai feature khác nhau không giẫm lên dữ liệu của nhau
  • Observability — trace, log, error, metric, lọc được riêng cho từng preview

Bạn đặt default dùng chung một lần trong wrangler.json, rồi override riêng cho từng môi trường mà không đụng tới production:

{
  "vars": { "ENVIRONMENT": "production" },
  "previews": {
    "vars": { "ENVIRONMENT": "preview" }
  }
}

Đây là một mô hình sạch. Và cũng chính là mô hình mà team nào cũng từng cố tự xây bằng tay, với hậu tố tên môi trường (api-staging-jsmith, api-pr-4821) cùng một quy ước sẽ mục nát ngay ngày đầu tiên có người quên mất nó. Cloudflare giờ biến quy ước đó thành chức năng của nền tảng.

Lỗ hổng: service binding vẫn gọi production

Đây là câu nên dừng bạn lại trước khi gắn Previews vào pipeline CI rồi gọi nó là “staging đầy đủ”: một service binding khai báo trong Preview Worker vẫn resolve tới bản deploy production của Worker được bind, không phải bản preview của Worker đó. Queues có một phiên bản nhẹ hơn của cùng vấn đề — Preview publish message được, nhưng chưa có consumer cô lập riêng. Workflows thì không tự động cô lập theo branch chút nào, phải config riêng.

Hình dung topology thực tế mà nhiều team chạy Workers đang có:

checkout-worker (Preview, branch: add-promo-codes)
  └─ service binding → pricing-worker

pricing-worker (production)

Bạn deploy branch promo-code, test checkout từ đầu đến cuối, mọi thứ pass — vì pricing-worker ở production vẫn vô tư tính giá mà không hề có logic promo nào cả, vì logic đó chưa tồn tại ở đó. Preview báo tính năng chạy được. Thực ra không, không chạy được ở bất kỳ môi trường nào phản ánh đúng thứ bạn sắp ship. Preview cô lập đúng chỗ bạn đang sửa, và âm thầm gắn chặt với production ở chỗ bạn không để ý tới.

Đây là kiểu lỗi giống hệt việc mock một service downstream trong unit test rồi đem thẳng giả định của cái mock đó lên prod — chỉ khác là nó trông giống integration test chứ không phải unit test, nên được tin tưởng hơn mức nó xứng đáng.

Chỗ này thực sự cắn người ta

Mình từng thấy đúng dạng lỗi này ở ba stack khác hẳn nhau, chẳng liên quan gì tới Cloudflare: Kubernetes namespace mà một ClusterIP service lỡ resolve xuyên namespace, Terraform workspace dùng chung remote state backend mà chẳng ai cô lập, docker-compose stack mà một sidecar âm thầm trỏ vào URL database prod bị bake cứng trong base image. Cô lập không bao giờ là một thuộc tính nhị phân “có” hay “không” của cả môi trường — nó cô lập theo từng loại resource, và loại resource ít khi được cô lập đầu tiên chính là loại kết nối các service với nhau.

Nếu kiến trúc Worker của bạn chỉ là một Worker với Durable Objects và KV, Previews cho bạn gần như một staging thật, miễn phí, theo từng branch, ngay hôm nay. Nếu kiến trúc của bạn là cả chục Worker gọi nhau qua service binding — đúng kiến trúc mà docs của Cloudflare khuyên dùng cho bất cứ thứ gì lớn hơn app demo — thì Previews hiện tại chỉ kiểm chứng từng Worker riêng lẻ, còn âm thầm nối cả hệ thống lại qua production. Vậy còn tệ hơn không có staging, vì không có staging ít ra còn thành thật báo bạn biết là đang test với không khí.

Mình sẽ làm gì với cái này ngay bây giờ

  1. Rà soát sơ đồ binding trước khi tin một Preview xanh. Nếu Worker đang test có service binding, ghi rõ ra bên nào được cô lập, bên nào không. Đừng giả định — docs không hiện cái này theo từng binding trong UI preview, bạn phải tự biết cách mình đấu dây.
  2. Vẫn smoke-test các luồng xuyên Worker trên một bản deploy staging thật cho đến khi Cloudflare cô lập được service binding cho Previews — coi Previews là “nhanh, an toàn để test đúng Worker mình đang sửa,” chứ không phải “nhanh, an toàn để test cả hệ thống.”
  3. Ghi rõ Queues và Workflows trong template PR nếu branch có đụng tới. “Đã test trong Preview” mang ý nghĩa khác nhau tùy primitive, và cái khác biệt đó sẽ không sống sót qua một buổi standup nếu không ai viết ra giấy.

Worker Previews giải quyết một vấn đề thật và khó chịu — cái vấn đề mà kỹ sư nào trong team cũng tự chế ra một phiên bản “bản sao app của riêng tôi để test” hơi sai một chút. Tính năng này tốt. Câu về service binding mới là thứ quyết định team bạn ship một cảm giác an toàn giả hay an toàn thật, và rất dễ bỏ sót vì nó đọc như một ghi chú phụ chứ không phải một sự thật mang tính nền tảng như nó vốn là.

Xuất nội dung

Bình luận