16 điểm bởi GN⁺ 2026-01-15 | 2 bình luận | Chia sẻ qua WhatsApp
  • Rails 8 đã loại bỏ sự phụ thuộc vào Redis khỏi stack mặc định, và chuyển sang xử lý mọi tác vụ trên cơ sở dữ liệu quan hệ (RDB) thông qua SolidQueue·SolidCache·SolidCable
  • Redis nhanh và ổn định, nhưng kéo theo độ phức tạp vận hành như cấu hình, bảo mật, quản lý cluster, sao lưu
  • SolidQueue tận dụng tính năng FOR UPDATE SKIP LOCKED của PostgreSQL để triển khai xử lý tác vụ song song không tranh chấp
  • Cung cấp miễn phí các tính năng vốn là tính năng trả phí của Redis+Sidekiq như tác vụ định kỳ, kiểm soát đồng thời, dashboard giám sát (Mission Control)
  • Phần lớn ứng dụng Rails chỉ cần SolidQueue là đủ; chỉ một số trường hợp cần xử lý thời gian thực siêu nhanh mới cần tiếp tục dùng Redis

Chi phí ẩn của Redis

  • Ngoài chi phí hosting đơn thuần, Redis còn tạo ra gánh nặng quản trị liên tục như cài đặt, bảo trì, cấu hình bảo mật, quản lý cluster HA
    • Cần có kết nối mạng và cấu hình tường lửa giữa Rails và Redis, xác thực client, cùng điều phối tiến trình Sidekiq
  • Khi sự cố xảy ra, phải debug đồng thời cả Redis và RDBMS, đồng thời cũng cần chiến lược sao lưu kép
  • Ngược lại, với stack Rails không có Redis, chỉ cần quản lý một PostgreSQL duy nhất, nên có thể đơn giản hóa đáng kể

Cách SolidQueue hoạt động

  • Sử dụng tính năng FOR UPDATE SKIP LOCKED của PostgreSQL để nhiều worker có thể lấy tác vụ cùng lúc mà không bị tranh chấp khóa (lock contention)
  • Cấu trúc bảng chính
    1. solid_queue_jobs: lưu metadata của tác vụ
    2. solid_queue_scheduled_executions: chờ các tác vụ đã lên lịch
    3. solid_queue_ready_executions: hàng đợi các tác vụ sẵn sàng thực thi
  • Các tiến trình worker, dispatcher, scheduler, supervisor phối hợp với nhau bằng cách polling định kỳ các bảng khác nhau
  • Nhờ thiết kế MVCC và autovacuum của PostgreSQL, hệ thống vẫn xử lý ổn định cả khi có khối lượng lớn thao tác insert và delete

Lập lịch tác vụ lặp lại

  • SolidQueue cung cấp sẵn tác vụ lặp theo kiểu cron, được cấu hình trong file config/recurring.yml
  • Scheduler sẽ đưa các tác vụ đến thời điểm chạy vào queue, đồng thời tự động lên lịch cho lần chạy tiếp theo
  • Dùng thư viện Fugit để phân tích lịch bằng ngôn ngữ tự nhiên, và tạo thread bằng Concurrent::ScheduledTask
  • Áp dụng cách lập lịch mang tính xác định (deterministic) của GoodJob để vẫn giữ đúng lịch ngay cả khi tiến trình khởi động lại

Tính năng kiểm soát đồng thời

  • SolidQueue dùng mẫu semaphore POSIX để hỗ trợ giới hạn số tác vụ chạy đồng thời theo từng đơn vị công việc
    • Ví dụ: khi cấu hình limits_concurrency to: 1, key: ->(user) { user.id }, mỗi người dùng chỉ có 1 tác vụ được chạy cùng lúc
  • Có thể đặt thời hạn hết hiệu lực của semaphore (duration) để ngăn xung đột tác vụ và deadlock
  • Các bảng liên quan
    • solid_queue_semaphores: theo dõi giới hạn đồng thời
    • solid_queue_blocked_executions: lưu các tác vụ đang chờ

Giám sát bằng Mission Control

  • Mission Control Jobs là dashboard mã nguồn mở miễn phí cho Rails 8, có thể mount đơn giản tại đường dẫn /jobs
  • Tính năng chính
    • Trạng thái queue theo thời gian thực, theo dõi tác vụ thất bại, điều khiển retry/discard
    • Trực quan hóa timeline của tác vụ đã lên lịch và tác vụ lặp
    • Biểu đồ throughput và metric theo từng queue
  • Hỗ trợ truy vấn dựa trên SQL, nên có thể phân tích trực tiếp trong cơ sở dữ liệu mà không cần công cụ bổ sung

Migration từ Sidekiq sang SolidQueue

  • Bước 1: đặt config.active_job.queue_adapter = :solid_queue
  • Bước 2: chạy bundle add solid_queue, sau đó rails solid_queue:installdb:migrate
  • Bước 3: chuyển lịch cron trong sidekiq.yml sang recurring.yml
  • Bước 4: thêm jobs: bundle exec rake solid_queue:start vào Procfile
  • Bước 5: xóa các gem liên quan đến Redis và Sidekiq
  • Mã ActiveJob hiện có vẫn hoạt động nguyên vẹn mà không cần chỉnh sửa

Khi nào vẫn cần Redis

  • Xử lý liên tục hàng nghìn tác vụ mỗi giây trở lên
  • Hệ thống thời gian thực bắt buộc có độ trễ dưới 1ms
  • Cần cấu trúc pub/sub phức tạp hoặc rate limiting và phép đếm tinh vi
  • Ví dụ, Shopify vận hành 833 request mỗi giây và 1.172 tiến trình worker, sử dụng hạ tầng Redis

Hướng dẫn triển khai thực tế

  • Khi tạo app Rails 8 mới, SolidQueue·SolidCache·SolidCable sẽ được cấu hình tự động
  • Khuyến nghị cấu hình kết nối cơ sở dữ liệu queue riêng trong config/database.yml
  • Thêm xác thực cho Mission Control và mount route /jobs
  • Thêm jobs: bundle exec rake solid_queue:start vào Procfile.dev, rồi chạy toàn bộ hệ thống bằng bin/dev
  • Sau khi tạo tác vụ thử nghiệm, có thể kiểm tra trạng thái trong Mission Control

Các vấn đề thường gặp và cách xử lý

  • Vẫn có thể dùng cấu hình một cơ sở dữ liệu duy nhất, nhưng tính linh hoạt khi vận hành sẽ giảm
  • Trong môi trường production, bắt buộc phải thêm xác thực cho Mission Control
  • Khoảng polling mặc định là 1 giây cho tác vụ đã lên lịch, 0,2 giây cho tác vụ tức thời, phù hợp với phần lớn ứng dụng
  • Khi dùng ActionCable/Turbo Streams, cần cấu hình SolidCable với kết nối DB riêng

Khả năng mở rộng và hiệu năng

  • SolidQueue có thể mở rộng đủ tốt cho phần lớn ứng dụng Rails
  • Dựa trên PostgreSQL, hệ thống có thể xử lý 200–300 tác vụ mỗi giây, và 37signals xử lý 20 triệu tác vụ mỗi ngày mà không cần Redis
  • Bảng so sánh
    Hạng mục Redis + Sidekiq SolidQueue
    Độ phức tạp cấu hình Cần dịch vụ riêng Dùng DB tích hợp
    Ngôn ngữ truy vấn Lệnh Redis SQL
    Giám sát Dashboard riêng Mission Control
    Kịch bản sự cố Từ 6 trở lên 2
    Thông lượng Hàng nghìn tác vụ/giây 200–300 tác vụ/giây
    Đối tượng phù hợp 99,9% ứng dụng 95% ứng dụng

Kết luận

  • Redis và Sidekiq là các công nghệ rất xuất sắc, nhưng với phần lớn ứng dụng Rails, chúng gây ra độ phức tạp và chi phí vượt mức cần thiết
  • SolidQueue hiện thực hóa đơn giản hóa vận hành, cắt giảm chi phí và tăng hiệu quả bảo trì trên nền tảng một cơ sở dữ liệu duy nhất
  • Trong kỷ nguyên Rails 8, chuyển sang SolidQueue được khuyến nghị như lựa chọn mặc định

2 bình luận

 
kaydash 2026-01-17

Redis thì tốt đấy.

 
GN⁺ 2026-01-15
Ý kiến trên Hacker News
  • Tôi nghĩ mọi tác giả mã nguồn mở đều có quyền kiểm soát phạm vi dự án của mình
    Nhưng nhóm chúng tôi đang hối hận vì đã chuyển từ good_job sang SolidQueue
    Basecamp lấy MySQL làm trung tâm nên không chấp nhận các truy vấn tối ưu riêng cho từng engine RDBMS. Chỉ cần nhìn vào GitHub issue là thấy họ chỉ tập trung vào hiệu năng của MySQL
    Ngoài ra hiện vẫn chưa có hỗ trợ batch job (PR liên quan)

    • Nghe như một sự kết hợp tệ nhất. Bên tôi cũng dùng MySQL, nhưng không thể sống thiếu các truy vấn đặc thù theo engine
      Trong các JOIN phức tạp, MySQL khá hay lập sai query plan, nên tôi dùng STRAIGHT_JOIN để ép thứ tự. Coi như để phòng tương lai
    • Nếu đã bị ràng buộc sâu với MySQL như vậy, chẳng phải dùng luôn tính năng riêng của MySQL sẽ hợp lý hơn sao? Có vẻ như đang thiếu điều gì đó
    • Tôi muốn nghe cụ thể hơn lý do vì sao bạn khuyên dùng GoodJob hơn SolidQueue
      Tôi đang so sánh hai lựa chọn này để chuyển từ resque. GoodJob không tương thích với chế độ transaction của pgbouncer do dùng tính năng riêng của pg
      Việc phải duy trì session liên tục thì khá phiền, nhưng mức cải thiện hiệu năng ở phần lớn quy mô lại không quá có ý nghĩa
      Dù vậy, mô hình phát triển và độ dễ đọc của mã trong GoodJob tạo cảm giác đáng tin cậy hơn hẳn
    • Đồng ý. good_job là cách tiếp cận lý tưởng cho một hàng đợi dựa trên Postgres
    • Tôi chưa hiểu rõ SolidQueue lắm, nhưng good_job thì dùng rất thích. Nó hoạt động rất tốt
  • Nếu có thể làm đơn giản môi trường production thì lúc nào cũng là điều tốt
    Tôi nghĩ tình huống lý tưởng trong Rails là có một cấu trúc có thể chuyển sang Redis dễ dàng
    Sẽ rất tốt nếu có thể bắt đầu với SolidQueue rồi khi chạm trần khả năng mở rộng thì chuyển sang Redis
    Phần lớn ứng dụng Rails không có lưu lượng lớn, nên việc duy trì cả hai hệ thống lại còn phức tạp hơn

    • Rails cung cấp Active Job như một API trừu tượng hóa, nên việc chuyển sang Redis khá dễ
      Tất nhiên cũng có ứng dụng phụ thuộc vào một hiện thực hàng đợi cụ thể, nhưng trong trường hợp phổ biến thì chỉ cần đổi cấu hình
    • Tôi tò mò liệu chế độ Redis AOF có ghi lại mọi thay đổi giống như WAL không
      Nó có dùng thêm snapshot để tránh log phình quá lớn không, và liệu điều này có hoạt động trong chế độ phân tán hay không
    • Nếu phụ thuộc quá nhiều vào transaction thì việc migration sẽ khó hơn
      Đặc biệt khi việc tạo job diễn ra cùng với các thay đổi DB khác, vấn đề là sẽ mất đi sự bảo đảm đó
    • Rails không tách bộ xử lý background job khỏi DB production, nên khá dễ gây nhầm lẫn
      Redis trước đây có lợi thế ở điểm này vì nó là một kho trạng thái nhẹ và độc lập
      Có vẻ SolidQueue không làm rõ ranh giới tách biệt này (riverqueue.com)
  • Tôi đã thử nghiệm SolidQueue trong dự án phụ của mình
    Kết luận là, nếu Sidekiq không có vấn đề gì thì không có lý do phải đổi
    Chỉ đáng cân nhắc khi bạn muốn bỏ hạ tầng Redis
    Nếu là dự án mới thì GoodJob có vẻ trưởng thành hơn và cộng đồng cũng tốt hơn
    Tôi thấy UI của SolidQueue quá đơn giản nên khá bất tiện. Do không tối ưu index, khi dữ liệu nhiều thì trang bị treo
    Cũng cần tính đến việc dùng RDBMS sẽ phát sinh thêm chi phí quản lý connection pool

  • Với những người lo về khả năng mở rộng, xem benchmark của Oban trong Elixir thì
    một node đơn có thể xử lý một triệu job mỗi phút. Hầu hết ứng dụng có tải thấp hơn rất nhiều

    • Công ty chúng tôi cũng dùng Oban, và Oban dùng Redis làm notifier hoặc khuyến nghị polling (tài liệu scaling)
    • Nhưng benchmark đó khá xa thực tế ứng dụng
      Nó nhét 5000 job theo từng lô một lần, nên TPS thực tế chỉ khoảng 200
      Nếu đẩy từng job riêng lẻ không theo lô thì tải transaction SQL sẽ cao hơn nhiều
  • Chúng tôi đã lưu job trong DB từ trước cả khi có SolidQueue
    Ưu điểm là có thể snapshot nguyên trạng thái production sang môi trường phát triển
    Tuy nhiên rate limiter thì vẫn để ở Redis. Làm vậy để tránh tăng tải cho DB

  • Giới hạn của hàng đợi dựa trên DB là payload lớn
    Nếu nhét JSON lớn vào hàng đợi thì sẽ kém hiệu quả vì overhead ghi DB
    Redis (Sidekiq) nhanh hơn nhiều trong trường hợp này
    SolidQueue+SQLite vẫn ổn nếu chỉ dùng để truyền primary key
    Nhưng nếu nhiều worker cùng polling một DB thì sẽ nhanh chóng thành nút thắt cổ chai

    • Trên thực tế, cách phổ biến là chỉ truyền một hoặc hai ID làm tham số job. Nhiều hơn thế thường là không hiệu quả
    • Redis cũng không phù hợp với payload lớn vì giới hạn bộ nhớ
      Tôi nghĩ dữ liệu lớn nên để ở storage bên ngoài như S3 rồi chỉ truyền tham chiếu
    • Dù sao các phần khác của hệ thống cũng sẽ cần storage, nên dùng kho tạm như S3 hay garage là giải pháp thực tế
      Tôi khá tò mò không biết có tài liệu nào tổng hợp kết quả benchmark không
    • Tài liệu Sidekiq cũng khuyến nghị chỉ truyền định danh
    • Lưu payload lớn trong Redis là thực hành tồi vì bộ nhớ là hữu hạn
  • SolidQueue có nhắc đến SKIP LOCKED, nhưng giữ transaction cho một job kéo dài 15 phút là rất rủi ro
    Transaction mở quá lâu sẽ phá hiệu năng DB và cũng dễ tổn thương trước sự cố mất kết nối mạng
    Kiểu kiến trúc này có thể dẫn đến phản mẫu. Sau đó tôi mới thấy có vẻ nó dùng cơ chế lease

  • Tôi đồng cảm với triết lý Postgres for everything
    Tôi nghĩ đơn giản hóa bằng cách hợp nhất vào một PostgreSQL duy nhất là điều tốt

    • Tôi cũng thích ý tưởng all-in với PG, nhưng thường nghe câu “khi bạn chỉ có cái búa, mọi thứ trông như cái đinh”
      Tôi không biết nên phản biện ẩn dụ này thế nào
    • Dạo này storage NVMe nhanh đến mức tôi thấy lợi thế của Redis đang giảm đi
      Tôi tự hỏi có còn lý do gì để dùng Redis nếu phải đánh đổi bằng việc tăng độ phức tạp
  • “Doanh nghiệp mà độ trễ dưới 1ms là quan trọng”, ý là chạy HFT bằng Rails sao?

    • Tất nhiên là không. Nếu xem trường hợp của SimpleThread thì công ty đó khá xa HFT
    • Có thể không chạy trading engine bằng Rails, nhưng UI giám sát giao dịch thì hoàn toàn có thể làm bằng Rails
  • Postgres sẽ nuốt chửng thế giới

    • Tôi cũng đồng ý. Biết đâu một ngày nào đó sẽ có extension pg_kernel, rồi chúng ta xóa luôn Linux
    • Nhưng vài năm nữa có khi chúng ta nhận ra “Postgres for everything” cũng chỉ là một trào lưu quá tay giống “MongoDB for everything”
    • Dù vậy, MySQL vẫn dễ bảo trì và hiệu năng cũng ổn (tôi nói vậy dù là người dùng Postgres)
    • Hiện tôi dùng PGQM và PG_CRON, và nó gọn gàng hơn hẳn bộ MySQL + Redis + AWS trước đây
    • Cuối cùng điều quan trọng vẫn là bộ tính năng. RDBMS sẽ nuốt chửng thế giới