3 điểm bởi GN⁺ 2026-01-30 | 1 bình luận | Chia sẻ qua WhatsApp
  • Oban.pyphiê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ồngxử 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.pykhung 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

 
GN⁺ 2026-01-30
Ý 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

    • Cảm ơn, Mike! Anh thực sự là nguồn cảm hứng. Parker và tôi có những thế mạnh khác nhau, và chúng tôi tin vào sức mạnh cộng hưởng đến từ khả năng tương tác giữa Python và Elixir
    • Faktory là nguồn cảm hứng lớn cho hệ thống hàng đợi tác vụ độc lập ngôn ngữ mà tôi tạo ra, Ocypod. Cảm ơn vì đã phát hành nó dưới dạng mã nguồn mở
    • Thực ra nói cả hai đều dựa trên Resque thì có lẽ chính xác hơn không?
    • Có thể không phải chủ ý, nhưng bình luận của bạn trông như đang quảng bá dự án của mình. Ở đây kiểu đó không được đánh giá cao lắm
    • Cụm “based on” có vẻ hơi cường điệu. Sidekiq đơn giản hơn nhiều nếu so với các workflow, cron, partitioning, tác vụ phụ thuộc, xử lý lỗi... mà Oban hỗ trợ
  • Đ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

    • Tôi cực kỳ đồng cảm với ý này. Trước đây tôi đã thấy sự kiện bị mất trong nhiều năm vì Dual Write Problem. Cuối cùng quay lại cách tiếp cận dựa trên SQL thì vấn đề được giải quyết chỉ trong một ngà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
    • Đây chính là thứ được gọi là Transactional Outbox Pattern
    • Tôi cũng thấy tính năng này thực sự rất hay. Nhưng thay vào đó tôi đang dùng pg_timetable
    • Chúng tôi đang xây dựng một trình tạo ứng dụng dùng AI, và sử dụng Elixir, Phoenix, và tất nhiên là OBAN.
      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
    • Tôi chưa từng nghĩ về transaction theo cách này, thật sự rất ấn tượng
  • Độ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

    • Tùy theo mức độ quan tâm và sử dụng, một số tính năng có thể được chuyển sang OSS. Chúng tôi đã từng làm vậy ở Elixir.
      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
    • Để tham khảo, Django 6.0 đã bổ sung Django Tasks API cho phép thay backend, nhưng hiện vẫn chưa có backend production-ready thực sự.
      Nếu thêm hỗ trợ Django Tasks vào Chancy hoặc tạo gói django-chancy thì có lẽ sẽ được chấp nhận rất nhanh
    • Cảm ơn vì đã chia sẻ Chancy. Trông khá thú vị. Ngoài các thay đổi API ra thì hiện giờ nó đã đủ ổn định để dùng production chưa?
  • Bả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

    • Tôi cũng thấy vậy. Oban rất tuyệt, nhưng việc “phiên bản tốt hơn của cùng một tính năng” lại là trả phí thì hơi tiếc.
      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ác hàng đợi dựa trên Redis hay RabbitMQ có thể làm mất tác vụ sau khi bị dừng đột ngột.
      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

  • 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

    • Hai công cụ này có triết lý hoàn toàn khác nhau.
      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
    • Chúng tôi đã chuyển từ Celery sang Prefect và khá hài lòng. Nó hoàn hảo cho xử lý tác vụ ở quy mô hàng nghìn.
      Hiện chúng tôi dùng kết hợp ProcessWorker đơn giản và worker ECS
    • Tôi mới quay lại phát triển web Python sau một thời gian dài, và khá bất ngờ vì có nhiều bất mãn với Celery như vậy.
      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ó DBOSAbsurd.
    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

    • Ở Elixir cũng đã có những dự án OSS tương tự, và việc Oban triển khai workflow bền vững còn đi trước DBOS vài năm.
      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

    • Tôi muốn hỏi là trong khoảng thời gian nào. Tôi đang chạy Rails/Solid Queue + Postgres với 20 triệu tác vụ mỗi ngày trên một VM $45 và vẫn còn rất dư dả
  • 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?

    • Không có giới hạn về độ dài tác vụ. Khác biệt chỉ là tốc độ khôi phục sau sự cố.
      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