# Background Jobs trong Rails 2026: Sidekiq vs Good Job và Câu hỏi Phỏng vấn > Hướng dẫn toàn diện về việc chọn queue backend cho Rails: so sánh Sidekiq 8.x, Good Job 4.x và Solid Queue với trọng tâm vào throughput, tính năng và câu hỏi phỏng vấn kỹ thuật. - Published: 2026-09-12 - Updated: 2026-09-12 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Background jobs trong Rails đã phát triển đáng kể với Rails 8.x và việc giới thiệu Solid Queue làm backend Active Job mặc định. Việc lựa chọn giữa Sidekiq, Good Job và Solid Queue phụ thuộc vào yêu cầu throughput, ràng buộc hạ tầng và các tính năng mà mỗi dự án cần. > **Hướng dẫn Quyết định Nhanh** > > Đối với các dự án Rails 8 mới, hãy bắt đầu với Solid Queue. Chuyển sang Sidekiq cho workload cao vượt quá 100.000 jobs/ngày hoặc khi độ trễ cấp Redis quan trọng. Chọn Good Job cho jobs dựa trên PostgreSQL với tính năng batch và unique job constraints có sẵn. ## Active Job Thống nhất Background Processing như thế nào Active Job cung cấp interface chuẩn để khai báo, xếp hàng và thực thi công việc background trong Rails. Framework này trừu tượng hóa sự khác biệt giữa các queue backend, cho phép code có thể di chuyển giữa Sidekiq, Good Job, Solid Queue hoặc bất kỳ adapter nào khác. ```ruby # app/jobs/payment_processor_job.rb class PaymentProcessorJob < ApplicationJob queue_as :critical retry_on Stripe::RateLimitError, wait: :polynomially_longer, attempts: 5 discard_on Stripe::InvalidRequestError def perform(order_id) order = Order.find(order_id) PaymentService.process(order) OrderMailer.confirmation(order).deliver_later end end ``` Các callback `retry_on` và `discard_on` xử lý các lỗi tạm thời và lỗi vĩnh viễn mà không cần code đặc thù cho backend. Directive `queue_as` gán độ ưu tiên, mà mỗi backend diễn giải theo chiến lược xử lý queue riêng của mình. ## Sidekiq 8.x: Throughput dựa trên Redis [Sidekiq](https://github.com/sidekiq/sidekiq) vẫn là baseline hiệu suất cho background jobs Rails. Dòng phát hành 8.x yêu cầu Ruby 3.2+, Rails 7.0+ và Redis 7.2+ (Valkey và Dragonfly có thể được sử dụng như thay thế trực tiếp kể từ khi Redis thay đổi giấy phép). ```ruby # config/initializers/sidekiq.rb Sidekiq.configure_server do |config| config.redis = { url: ENV.fetch('REDIS_URL') } config.concurrency = 10 end Sidekiq.configure_client do |config| config.redis = { url: ENV.fetch('REDIS_URL') } end ``` Sidekiq 8 mang đến ba thay đổi lớn: Web UI được viết lại từ đầu giảm CSS từ 160KB xuống 16KB và thời gian render trang trung bình từ 55ms xuống 3ms, job profiling với [Vernier](https://github.com/jhawthorn/vernier) qua tab Profiles mới, và job metrics được lưu giữ đến 72 giờ. ### Khi nào Sidekiq phù hợp Sidekiq phù hợp với workload cần throughput bền vững, độ trễ pickup job dưới mili-giây, hoặc các tính năng enterprise như rate limiting và unique jobs biện minh cho việc chạy Redis. Capsules, được giới thiệu trong 7.0 và hiện là tiêu chuẩn, cho phép một tiến trình Sidekiq chạy nhiều thread pool cô lập với concurrency và queues riêng. ```ruby # config/sidekiq.yml :concurrency: 10 :queues: - [critical, 3] - [default, 2] - [low, 1] # Capsules cho xử lý cô lập :capsules: imports: :concurrency: 2 :queues: - imports ``` API embedding cũng cho phép Sidekiq chạy bên trong tiến trình Puma cho các ứng dụng nhỏ, tương tự như cách plugin Puma của Solid Queue hoạt động. ## Good Job 4.x: Batch và Uniqueness Native PostgreSQL [Good Job](https://github.com/bensheldon/good_job) sử dụng LISTEN/NOTIFY của PostgreSQL để pickup job và advisory locks để đảm bảo run-once. Phiên bản 4.19.x (hiện tại tính đến tháng 9 năm 2026) hỗ trợ Rails 6.1+ và Ruby 3.0+. ```ruby # config/application.rb config.active_job.queue_adapter = :good_job # config/initializers/good_job.rb Rails.application.configure do config.good_job.execution_mode = :async config.good_job.max_threads = 5 config.good_job.poll_interval = 5 config.good_job.shutdown_timeout = 25 end ``` Good Job cung cấp batches và unique jobs trong tier open-source, các tính năng mà Sidekiq dành riêng cho giấy phép trả phí. Lệnh gọi `perform_later` chèn một hàng vào bảng `good_jobs` trong cùng transaction database với code gọi, vì vậy nếu transaction rollback, job không bao giờ được tạo. ### Batches không cần Giấy phép ```ruby # app/jobs/batch_import_job.rb class BatchImportJob < ApplicationJob def perform(batch, params) batch.on(:finish, ImportCompletionJob, params) params[:file_ids].each do |file_id| batch.add do ProcessFileJob.perform_later(file_id) end end end end # Enqueue batch GoodJob::Batch.enqueue do |batch| BatchImportJob.perform_later(batch, file_ids: [1, 2, 3]) end ``` ### Unique Jobs cho Idempotency ```ruby # app/jobs/sync_inventory_job.rb class SyncInventoryJob < ApplicationJob include GoodJob::ActiveJobExtensions::UniqueJob good_job_control_concurrency_with( perform_limit: 1, key: -> { "sync_inventory_#{arguments.first}" }, duration: 1.hour ) def perform(warehouse_id) InventorySync.run(warehouse_id) end end ``` Ràng buộc `perform_limit: 1` đảm bảo chỉ một sync chạy cho mỗi warehouse tại bất kỳ thời điểm nào, ngăn chặn công việc trùng lặp khi cùng một job được enqueue nhiều lần. ## Solid Queue: Mặc định của Rails 8 [Solid Queue](https://github.com/rails/solid_queue) lưu trữ jobs trong database ứng dụng sử dụng `FOR UPDATE SKIP LOCKED`, một tính năng của PostgreSQL và MySQL biến bảng thông thường thành job queue. Các ứng dụng Rails 8 mới được cấu hình sẵn với Solid Queue mặc định. ```ruby # config/solid_queue.yml production: dispatchers: - polling_interval: 1 batch_size: 500 workers: - queues: "*" threads: 5 polling_interval: 0.1 ``` Solid Queue có thể chạy bên trong tiến trình web server Puma với `SOLID_QUEUE_IN_PUMA=true`, đơn giản hóa deployment cho các ứng dụng không cần tiến trình worker riêng biệt. Jobs được lưu trữ trong bảng database, tồn tại qua restart tiến trình và deployment mà không có dependency bên ngoài. ## Bảng So sánh Tính năng | Tính năng | Sidekiq 8.x | Good Job 4.x | Solid Queue 1.x | |-----------|-------------|--------------|------------------| | Backend | Redis 7.2+ | PostgreSQL | PostgreSQL/MySQL/SQLite | | Batches | Pro/Enterprise | Bao gồm | Đang lên kế hoạch | | Unique jobs | Enterprise | Bao gồm | Thủ công qua locks | | Cron scheduling | Enterprise | Bao gồm | Qua solid_queue_cron | | Web UI | Bao gồm | Bao gồm | Tối thiểu (Mission Control) | | Giới hạn throughput | 100k+ jobs/phút | 10k jobs/phút | 10k jobs/phút | | Embed trong Puma | Có (8.x) | Không | Có | | Giấy phép | LGPL + Commercial | MIT | MIT | ## Câu hỏi Phỏng vấn về Rails Background Jobs Phỏng vấn kỹ thuật thường kiểm tra sự hiểu biết về độ tin cậy của job, chiến lược retry và quyết định kiến trúc. Dưới đây là các câu hỏi phân biệt ứng viên có kinh nghiệm. ### Q1: `retry_on` khác với logic retry đặc thù backend như thế nào? `retry_on` là callback Active Job hoạt động trên tất cả các backend. Nó bắt các exception cụ thể, áp dụng chiến lược wait (`:polynomially_longer` lùi theo cấp số mũ), và enqueue lại job. Logic retry đặc thù backend, như `sidekiq_retry_in` của Sidekiq, chỉ hoạt động với backend đó và có thể override hành vi của Active Job. ```ruby # Active Job retry (portable) class ApiSyncJob < ApplicationJob retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 8 def perform(record_id) ExternalApi.sync(record_id) end end ``` ### Q2: Điều gì xảy ra với job nếu transaction database enqueue nó bị rollback? Với các queue dựa trên database (Good Job, Solid Queue), hàng job là một phần của transaction, vì vậy nó rollback cùng với mọi thứ khác. Với các queue dựa trên Redis (Sidekiq), job đã có trong Redis trước khi transaction hoàn thành, tạo ra orphaned jobs. Giải pháp là callback `after_commit`: ```ruby # Pattern đúng cho Sidekiq class Order < ApplicationRecord after_commit :enqueue_confirmation, on: :create private def enqueue_confirmation OrderConfirmationJob.perform_later(id) end end ``` ### Q3: Khi nào Good Job vượt trội hơn Sidekiq? Good Job vượt trội hơn Sidekiq khi chi phí vận hành Redis vượt quá lợi ích throughput của nó. Đối với các ứng dụng xử lý ít hơn 50.000 jobs/ngày, queue dựa trên PostgreSQL tránh được sự phức tạp về hạ tầng, monitoring và failover của một cluster Redis riêng biệt. Good Job cũng cung cấp batches và unique jobs mà không cần phí giấy phép, điều quan trọng cho các team cần những tính năng này với ngân sách hạn chế. ### Q4: Giải thích idempotency của job và tại sao nó quan trọng cho retry Idempotency có nghĩa là chạy cùng một job nhiều lần tạo ra cùng kết quả như chạy một lần. Jobs phải idempotent vì lỗi mạng, crash tiến trình hoặc xử lý timeout có thể gây ra thực thi trùng lặp. Các kỹ thuật bao gồm unique constraints trong database, kiểm tra kết quả hiện có trước khi xử lý và sử dụng idempotency keys bên ngoài cho API thanh toán. ```ruby # Xử lý thanh toán idempotent def perform(order_id, idempotency_key) return if Payment.exists?(idempotency_key: idempotency_key) Payment.create!( order_id: order_id, idempotency_key: idempotency_key, amount: Order.find(order_id).total ) end ``` ### Q5: Làm thế nào để xử lý jobs phải chạy chính xác một lần trên các distributed workers? Distributed locks ngăn chặn thực thi đồng thời. Good Job sử dụng advisory locks của PostgreSQL. Sidekiq Enterprise cung cấp unique jobs. Để triển khai thủ công, sử dụng Redis `SETNX` hoặc PostgreSQL `pg_try_advisory_lock`: ```ruby class ExclusiveJob < ApplicationJob def perform(resource_id) lock_key = "exclusive_job:#{resource_id}" ActiveRecord::Base.connection.execute( "SELECT pg_try_advisory_lock(hashtext('#{lock_key}'))" ).first['pg_try_advisory_lock'] or return begin process_resource(resource_id) ensure ActiveRecord::Base.connection.execute( "SELECT pg_advisory_unlock(hashtext('#{lock_key}'))" ) end end end ``` ## Chọn Backend phù hợp cho Rails Background Jobs - Bắt đầu với Solid Queue cho các dự án Rails 8 mới chưa có Redis trong stack - Chuyển sang Sidekiq khi khối lượng job vượt quá 50.000/ngày hoặc độ trễ pickup dưới 100ms là quan trọng - Chọn Good Job cho deployment chỉ PostgreSQL cần batches, unique jobs hoặc cron scheduling mà không cần chi phí giấy phép - Sử dụng callback `after_commit` khi enqueue jobs với Sidekiq để tránh orphaned jobs khi transaction rollback - Làm cho mỗi job idempotent vì retry và distributed processing có thể gây ra thực thi trùng lặp - Đối với phỏng vấn, thể hiện sự hiểu biết về transactional job enqueuing, chiến lược retry và các pattern distributed locking --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/ruby-on-rails/rails-background-jobs-sidekiq-good-job-comparison