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.

Background Jobs trong Rails 2026: Sidekiq vs Good Job và Câu hỏi Phỏng vấn

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_ondiscard_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 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 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.

Sẵn sàng chinh phục phỏng vấn Ruby on Rails?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Good Job 4.x: Batch và Uniqueness Native PostgreSQL

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 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ăngSidekiq 8.xGood Job 4.xSolid Queue 1.x
BackendRedis 7.2+PostgreSQLPostgreSQL/MySQL/SQLite
BatchesPro/EnterpriseBao gồmĐang lên kế hoạch
Unique jobsEnterpriseBao gồmThủ công qua locks
Cron schedulingEnterpriseBao gồmQua solid_queue_cron
Web UIBao gồmBao gồmTối thiểu (Mission Control)
Giới hạn throughput100k+ jobs/phút10k jobs/phút10k jobs/phút
Embed trong PumaCó (8.x)Không
Giấy phépLGPL + CommercialMITMIT

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

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

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
Thử thách hôm nay

Bạn có tìm ra lỗi trong Ruby on Rails không?

Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Anthony Fillion-Maillet

Viết bởi

Anthony Fillion-Maillet

Người sáng lập SharpSkill

Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.

Cập nhật ngày 12 tháng 9, 2026

Chia sẻ

Bài viết liên quan