BÀI VIẾT
Durable Object Là Session, Container Là Cơ Bắp: Đọc Kiến Trúc Streamline Của Cloudflare
Reference architecture mới của Cloudflare cho xử lý video tách một Worker, một Durable Object, và một Container thành ba việc khác nhau rõ ràng. Cái tách đó mới là bài học thật — layer nào làm việc gì, và khi nào bạn nên copy pattern này.

Trong bài viết
Cloudflare đăng một reference architecture tên Streamline ngày 2/10. Đó là một pipeline video — burn subtitle, overlay động cho livestream, ghép hình webcam. Ba use case mà đa số chúng ta sẽ không bao giờ tự build. Mình vẫn nghĩ bài này đáng đọc kỹ, vì cái đang được minh họa không phải là xử lý video. Mà là ranh giới giữa “state phải nhất quán” và “việc phải nặng”, và ranh giới đó xuất hiện trong mọi workload chạy lâu bạn từng thiết kế cho edge.
Ba layer, ba việc, không chồng lấn
Hình dạng là: một Worker đứng ngoài xử lý control signaling và auth Cloudflare Access, một Durable Object cho mỗi session đang active giữ logic orchestration, và một Container chạy binary Go bọc FFmpeg để làm việc encode/decode thật. Delivery qua RTMP, HLS, hoặc WebSocket tùy ai đang consume stream.
Đoạn đáng ngồi lại nghĩ: không layer nào trong ba cái này có thể gánh việc của layer khác mà không làm kiến trúc tệ đi.
Worker không thể giữ session state. Worker được thiết kế stateless — mỗi request có thể rơi vào isolate khác nhau, bất cứ đâu trong network Cloudflare. Đó chính xác là điều bạn muốn cho routing và auth check, và chính xác sai nếu dùng để “nhớ job encoding nào đang chạy cho stream này”. Nếu bạn cố track session state bằng KV store gọi từ Worker, bạn sẽ chiến đấu với eventual consistency ở từng lần update frame-state.
Durable Object không thể tự làm việc FFmpeg. Durable Object cho bạn một execution context single-threaded, nhất quán mạnh — một instance, một location, không có race condition trên state của chính nó. Hoàn hảo cho “session này còn active không, config encoding hiện tại là gì, route control message này đến đúng nơi”. Nhưng là nơi cực tệ để chạy transcode video tốn CPU, vì bạn đang đốt cái đảm bảo nhất quán duy nhất của mình vào việc không cần nó, và block logic orchestration trong lúc làm.
Container không thể là nguồn sự thật. Container ở đây là compute dùng-rồi-bỏ — spin lên, làm việc FFmpeg, bị kill và restart mà không ai quan tâm state bên trong nó. Nếu bạn đặt quyền sở hữu session vào trong container, bạn mất nó ngay khi container bị recycle.
Vậy cái tách đó không phải tùy tiện. Worker giữ identity và routing. Durable Object giữ sự thật về session và orchestration. Container giữ compute đắt đỏ, stateless-từ-ngoài-nhìn-vào. Mỗi layer làm đúng một việc nó thực sự được build cho.
Logic orchestration trông như thế nào
Cloudflare không đăng full source, nhưng hình dạng một Durable Object quản lý session kiểu Streamline tầm như thế này:
export class StreamSession {
state: DurableObjectState;
containerId: string | null = null;
async fetch(request: Request) {
const { type, payload } = await request.json();
switch (type) {
case "start":
this.containerId = await this.spinUpContainer(payload.config);
await this.state.storage.put("status", "encoding");
return new Response("started");
case "update-overlay":
// single-threaded nhờ đảm bảo của DO — không mất update
await this.forwardToContainer(this.containerId, payload);
return new Response("updated");
case "stop":
await this.tearDownContainer(this.containerId);
await this.state.storage.delete("status");
return new Response("stopped");
}
}
}
Dòng đáng chú ý là update-overlay. Một livestream có overlay động — bảng điểm, lower third, số người xem live — cần update được áp theo đúng thứ tự, không để hai request đồng thời đè lên nhau. Đó chính là vấn đề Durable Object giải quyết bằng chính cấu trúc của nó: một instance, một event loop, không cần tự viết lock. Container chỉ nhận “render config overlay này ngay” và làm việc pixel; nó không cần biết gì về đảm bảo thứ tự, vì DO đã enforce điều đó trước khi message đến.
Pattern này generalize ra ngoài video như thế nào
Bỏ FFmpeg ra, bạn có một pattern cho mọi workload chạy lâu, có state ở mức session, và nặng ở mức compute: server session game multiplayer, công cụ edit document cộng tác có bước render nặng, fleet thiết bị IoT mà mỗi thiết bị cần một hàng đợi lệnh có thứ tự nhưng xử lý thật là job nặng riêng, hoặc — gần với thứ nhiều người trong chúng ta thực sự build hơn — một layer orchestration cho agent mà Durable Object track state conversation/task còn Container chạy việc thực thi code sandbox thật.
Cái sai mình từng thấy team mắc với Durable Object là coi nó như một compute primitive đa năng sau khi đã mê cái đảm bảo nhất quán. Không phải vậy. Khoảnh khắc handler fetch của DO bạn tốn wall-clock thật cho việc tốn CPU thay vì routing và sổ sách, bạn đã lén nhét việc của Container vào một layer được định giá và thiết kế cho request nhanh, rẻ, hình dạng orchestration. Streamline là một ví dụ sạch của việc cố ý cự lại cám dỗ đó.
Bài học thật
Nếu bạn đang thiết kế thứ gì chạy lâu trên Workers, đừng bắt đầu bằng “làm sao cho cái này có state”. Bắt đầu bằng “phần nào thực sự cần nhất quán mạnh, phần nào chỉ cần chạy một lúc”. Hai câu đó gần như không bao giờ là cùng một đoạn code, và reference architecture của chính Cloudflare, nói cho cùng, là một ví dụ thực tế của việc cố ý giữ chúng tách biệt thay vì vớ lấy primitive nào thuận tay nhất lúc đó.



Thảo luận
Bình luận được duyệt trước khi công khai. Email của bạn được giữ riêng tư.