- Oban.py là phiên bản port sang Python của khung xử lý job Oban trong Elixir, dựa trên PostgreSQL, cho phép chèn và xử lý job chỉ bằng cơ sở dữ liệu
- Job được tạo và rollback trong transaction của cơ sở dữ liệu, đồng thời hỗ trợ nhiều tính năng như quản lý queue, lưu kết quả, lập lịch cron
- Bản mã nguồn mở có các giới hạn như chạy asyncio đơn luồng và xử lý chèn/xác nhận theo từng job, nhưng bản Pro cung cấp xử lý song song, workflow, smart concurrency
- Cơ chế hoạt động bên trong gồm năm bước
Insert → Notify → Fetch → Execute → Ack, và tận dụng PostgreSQL FOR UPDATE SKIP LOCKED để tránh xung đột đồng thời
- Việc bầu leader, khôi phục job mồ côi, retry với backoff... cũng đều được thực hiện dựa trên cơ sở dữ liệu, giúp xử lý phân tán ổn định mà không cần broker bên ngoài
Tổng quan về Oban.py
- Oban.py là khung xử lý job dựa trên cơ sở dữ liệu được port từ Oban của Elixir sang Python
- Job được chèn và xử lý trong transaction của cơ sở dữ liệu; nếu thất bại thì toàn bộ transaction sẽ bị rollback
- Bao gồm nhiều tính năng kiểm soát như giới hạn queue, lưu job đã hoàn tất, giữ lại kết quả, lập lịch cron
- Cung cấp hai phiên bản
- Mã nguồn mở (OSS): chạy asyncio đơn luồng, chèn/xác nhận từng job riêng lẻ, khôi phục đơn giản
- Bản Pro: xử lý song song dựa trên process pool, hỗ trợ workflow, relay, job duy nhất, smart concurrency
- OSS phù hợp cho dự án cá nhân hoặc mục đích đánh giá; với môi trường quy mô lớn thì nên dùng bản Pro
Luồng xử lý job
- Sau khi được chèn, job được lưu vào bảng
oban_jobs với state='available', và thông báo được gửi tới từng node thông qua NOTIFY của PostgreSQL
- Stager trên mỗi node phát hiện queue tương ứng để đánh thức Producer, rồi Producer lấy job và thực thi
- Khi chọn job, hệ thống dùng
FOR UPDATE SKIP LOCKED của SQL để xử lý song song mà không bị chạy trùng
- Các hàng đã bị khóa sẽ bị bỏ qua để producer khác có thể lập tức lấy job khác
- Job được dispatch dưới dạng async task, và khi hoàn tất sẽ xử lý acknowledgement thông qua callback
- Bản Pro dùng dispatcher dựa trên process pool thay cho asyncio để hỗ trợ thực thi song song trên nhiều lõi CPU
Các tiến trình nền
- Bầu leader (Leader Election)
- Xác định leader bằng
INSERT ... ON CONFLICT của PostgreSQL và cơ chế lease dựa trên TTL
- Không cần giao thức đồng thuận riêng; một leader duy nhất sẽ phụ trách dọn dẹp và khôi phục job
- Lifeline (khôi phục job mồ côi)
- Nếu một job đang chạy kéo dài quá một khoảng thời gian nhất định (
rescue_after, mặc định 5 phút), nó sẽ được khôi phục về trạng thái available
- Bản Pro kiểm tra producer còn sống hay không, còn OSS chỉ phán đoán dựa trên thời gian
- Pruner (dọn dẹp job)
- Xóa các job đã hoàn tất, bị hủy hoặc bị loại bỏ mà đã quá
max_age (mặc định 1 ngày)
- Giới hạn phạm vi xóa bằng
LIMIT để tránh gây tải cho cơ sở dữ liệu
Retry và backoff
- Khi job phát sinh ngoại lệ, Executor sẽ quyết định có retry hay không
- Nếu chưa vượt quá số lần thử tối đa (
max_attempts) thì retry, nếu vượt thì loại bỏ
- Backoff mặc định là tăng theo hàm mũ có kèm jitter
- Giúp tránh retry đồng loạt khi có nhiều job thất bại, từ đó giảm hiện tượng tăng tải đột biến (Thundering Herd)
- Ví dụ: lần 1 chờ khoảng 17 giây, lần 5 khoảng 47 giây, lần 10 khoảng 17 phút
- Lớp worker có thể triển khai logic backoff tùy chỉnh thông qua phương thức
backoff()
Đặc điểm chính và đánh giá
- PostgreSQL đóng vai trò trung tâm
- Thông qua
FOR UPDATE SKIP LOCKED, LISTEN/NOTIFY, ON CONFLICT, hệ thống xử lý toàn bộ điều khiển đồng thời, truyền tín hiệu, bầu leader
- Xây dựng tầng điều phối bằng một cơ sở dữ liệu duy nhất mà không cần Redis hay broker bên ngoài
- Không song song hóa nhưng vẫn hỗ trợ đồng thời
- Dựa trên asyncio nên phù hợp với các tác vụ I/O-bound; với tác vụ CPU-bound thì nên dùng bản Pro
- Cấu trúc mã rõ ràng
- Cách đặt tên nhất quán và phân tách trách nhiệm rõ ràng tạo nên codebase dễ đọc
- Phân vai rõ ràng giữa OSS và Pro
- OSS dành cho thử nghiệm/quy mô nhỏ, Pro dành cho môi trường quy mô lớn/hiệu năng cao
- Kết luận: Đây là bản port Python gọn gàng và có cấu trúc, hiện thực hóa một job queue hoàn chỉnh chỉ với PostgreSQL, phù hợp với người dùng Elixir hoặc các nhà phát triển muốn có hệ thống job không cần hạ tầng bên ngoài
1 bình luận
Ý kiến trên Hacker News
Tôi là người tạo ra Sidekiq, xin chúc mừng Shannon và Parker với bản phát hành này
Trước đây tôi cũng từng trăn trở điều tương tự — nên tập trung vào Ruby, hay mở rộng Sidekiq sang các ngôn ngữ khác. Tôi nhận ra mình không thể trở thành chuyên gia của mọi ngôn ngữ, nên thay vào đó đã tạo ra Faktory. Đây là mô hình trong đó một máy chủ trung tâm quản lý vòng đời của hàng đợi, còn client cho từng ngôn ngữ được giữ đơn giản. Ví dụ có client như faktory-rs. Nhược điểm là không tập trung vào một cộng đồng ngôn ngữ cụ thể, nên khó cung cấp ví dụ phù hợp với ngôn ngữ đó.
Có lẽ cách tiếp cận tập trung vào một cộng đồng sẽ mang lại kết quả tốt hơn. Thời gian sẽ trả lời
Điểm cốt lõi của Oban là có thể đưa và xử lý tác vụ chỉ bằng cơ sở dữ liệu. Bạn có thể chèn tác vụ gửi email ngay trong transaction tạo người dùng, và nếu thất bại thì toàn bộ sẽ rollback.
Nhiều người nói không nên dùng DB quan hệ làm job queue, nhưng họ bỏ qua tầm quan trọng của transaction. Bài viết Job Drain của Brandur Leach cũng giải thích rất rõ khái niệm này
Nhưng giờ chẳng còn ai nhớ sự bất tiện đó nữa. “Transactional Outbox Pattern” là điều bắt buộc, và tôi thích cách tiếp cận cho tôi đảm bảo ACID giống như dữ liệu của mình.
Ngay cả khi không hiểu nội bộ DB, chỉ cần đầu tư một tuần để học isolation level và thứ tự commit cũng có thể giúp bạn tiết kiệm một năm debug hệ thống phân tán
Trong thời đại có rất nhiều quy trình AI chạy lâu, kiểu độ bền này là điều thiết yếu. Ở các hệ sinh thái ngôn ngữ khác, bạn phải trả tiền để có tính năng như vậy, còn với Oban thì nó có sẵn
Đội ngũ Oban nổi tiếng trong hệ sinh thái Elixir vì kỹ thuật tinh xảo. Nhưng việc khóa process pool vào bản Pro thì khá khó hiểu.
Ví dụ gói $135/tháng bao gồm thực thi đa tiến trình, workflow, giới hạn toàn cục, unique job, tác vụ hàng loạt, nguồn mã hóa và hỗ trợ chuyên biệt.
Dự án Chancy của tôi hoàn toàn miễn phí, và cho phép trộn tự do asyncio, process, thread, và sub-interpreter.
Tôi nghĩ có lẽ nên đưa các tính năng này sang OSS, còn phần trả phí thì tập trung vào hỗ trợ doanh nghiệp. Hệ sinh thái Python có nhiều đối thủ hơn hẳn
Mô hình chỉ bán hỗ trợ đơn thuần trước đây không thật sự phù hợp, nhưng trong Python thì có thể khác.
Hệ sinh thái Python quả thật là nơi cái gì cũng nhiều
Nếu thêm hỗ trợ Django Tasks vào Chancy hoặc tạo gói
django-chancythì có lẽ sẽ được chấp nhận rất nhanhBản OSS của Oban chỉ hỗ trợ thực thi asyncio đơn luồng, nên các tác vụ CPU-bound sẽ chặn event loop.
Vì vậy tôi thấy nó không đáng để thử. Giao diện của Celery thì không hay lắm nhưng đã quen thuộc, và có thể scale dọc lẫn ngang gần như vô hạn.
Dù vậy, sau khi biết có thể chạy nhiều worker node thì tôi cũng thay đổi suy nghĩ phần nào
Việc phân biệt tính năng OSS/Pro thì không sao, nhưng câu “bản Pro theo dõi producer còn sống bằng heartbeat thông minh hơn” thì hơi đáng tiếc.
Việc các tính năng liên quan đến độ tin cậy là trả phí khiến dự án OSS khó được chấp nhận hơn
Bản cơ bản nên là bản tốt nhất, còn tính năng bổ sung mới nên thu phí. Có vẻ ranh giới đang nằm ở một chỗ hơi kỳ lạ
Câu được trích ở đây hơi thiếu chính xác — việc theo dõi producer sống hay chết là như nhau, điểm khác biệt chỉ là cách khôi phục tác vụ mồ côi
Tôi ước các workflow BI/ML/DS trong Python được chuyển sang Elixir.
Tôi nghĩ Elixir với tính hàm, chịu lỗi, và đồng thời cao là nền tảng tự nhiên hơn nhiều cho các công việc kiểu này
Video này và hướng dẫn Elixir Genius là tài liệu tham khảo tốt
Công ty chúng tôi cũng đang dùng Celery, nhưng không thực sự thích lắm. Temporal thì quá nặng, còn Oban thì nhẹ và khá hợp ý tôi.
Tôi muốn nghe so sánh từ những ai đã dùng cả hai
Temporal phù hợp với những tổ chức có thể chấp nhận độ phức tạp để đổi lấy đảm bảo workflow, ví dụ như ngân hàng.
Oban là hàng đợi dựa trên DB, nên bạn phải tự tăng cường độ tin cậy.
Tôi nghĩ sẽ tốt nếu trong hệ thống có cả hai
Hiện chúng tôi dùng kết hợp ProcessWorker đơn giản và worker ECS
Tôi tự hỏi gần đây Celery có trở nên kém ổn định hoặc khó xử lý hơn không
Dự án thú vị đấy. Tuy vậy có thể thấy một số tính năng cốt lõi chỉ có trong bản Pro.
Các dự án đi trước đã triển khai workflow bền vững dựa trên Postgres dưới dạng OSS có DBOS và Absurd.
Thật đáng mừng khi thấy cách tiếp cận lấy cơ sở dữ liệu làm trung tâm ngày càng phổ biến
Một mô hình hoàn toàn mã nguồn mở và chỉ sống bằng bán hỗ trợ là mô hình như mơ. Hy vọng một ngày nào đó điều đó sẽ khả thi
Tôi tò mò liệu Postgres có đủ hiệu năng để xử lý hàng trăm triệu tác vụ hay không. Trước đây tôi từng thấy hiệu năng tăng mạnh khi chuyển sang Redis + Sidekiq
Bản OSS được nói là nếu có tác vụ dài thì ngay cả khi producer vẫn còn sống, nó vẫn có thể bị khôi phục nhầm.
Vậy nghĩa là chỉ nên dùng cho tác vụ ngắn thôi sao?
Thời điểm khôi phục chỉ khác khi không thể chờ quá trình tắt bình thường