Solid Queue i Solid Cache w Rails 8: Kompleksowy przewodnik przed rozmową techniczną 2026

Solid Queue i Solid Cache eliminują zależność od Redis w Rails 8. Kompletny przewodnik po architekturze, konfiguracji, kontroli współbieżności i najczęstszych pytaniach rekrutacyjnych na 2026 rok.

Solid Queue i Solid Cache w Rails 8 — przewodnik techniczny

Wraz z premierą Rails 8 nastąpiła fundamentalna zmiana w podejściu do infrastruktury aplikacji Ruby on Rails. Solid Queue i Solid Cache — dwa nowe komponenty dostarczane domyślnie z frameworkiem — zastępują Redis jako silnik kolejek zadań oraz system buforowania. Zamiast utrzymywać dodatkową usługę in-memory, aplikacja opiera się wyłącznie na relacyjnej bazie danych, co radykalnie upraszcza stos technologiczny.

Solid Trifecta — trójka domyślnych komponentów

Rails 8 wprowadza trzy komponenty oparte na bazie danych: Solid Queue (zadania w tle), Solid Cache (buforowanie) oraz Solid Cable (WebSocket). Razem eliminują one konieczność konfigurowania i utrzymywania Redis w większości projektów. To zagadnienie pojawia się regularnie na rozmowach technicznych dotyczących Rails 8.

Mechanizm działania Solid Queue — jak baza danych zastępuje Redis

Solid Queue to silnik Active Job oparty na relacyjnej bazie danych. Zadania przechowywane są bezpośrednio w PostgreSQL, MySQL lub SQLite. Kluczowym mechanizmem jest klauzula SQL FOR UPDATE SKIP LOCKED — dostępna w PostgreSQL od wersji 9.5 i MySQL od wersji 8 — która pozwala wielu procesom jednocześnie pobierać zadania z tej samej tabeli bez wzajemnego blokowania.

Architektura składa się z trzech elementów:

  • Workers — odpytują tabelę solid_queue_ready_executions i rezerwują zadania za pomocą SKIP LOCKED
  • Dispatchers — przenoszą zaplanowane zadania z solid_queue_scheduled_executions do tabeli gotowych, gdy nadejdzie ich czas wykonania
  • Schedulers — obsługują cykliczne zadania zdefiniowane w konfiguracji
ruby
# config/solid_queue.yml
production:
  dispatchers:
    - polling_interval: 1
      batch_size: 500
  workers:
    - queues: "*"
      threads: 5
      polling_interval: 0.1
      processes: 2

Powyższa konfiguracja uruchamia dwa procesy workerów, każdy z 5 wątkami, odpytujące bazę co 100 ms. Dispatcher sprawdza zaplanowane zadania co sekundę, przetwarzając partie po 500 rekordów.

Transakcyjne kolejkowanie zadań — gwarancja spójności danych

Jednym z najsilniejszych argumentów za Solid Queue jest transakcyjne kolejkowanie. Gdy zadanie zostaje dodane do kolejki wewnątrz transakcji bazodanowej, staje się widoczne dopiero po pomyślnym zatwierdzeniu tej transakcji. Silniki oparte na Redis nie mogą tego zagwarantować — zadanie mogłoby zostać wykonane zanim transakcja, która je utworzyła, dobiegnie końca, co prowadzi do odwoływania się do nieistniejących jeszcze rekordów.

ruby
# app/jobs/send_welcome_email_job.rb
class SendWelcomeEmailJob < ApplicationJob
  self.enqueue_after_transaction_commit = true

  queue_as :default

  def perform(user_id)
    user = User.find(user_id)
    UserMailer.welcome(user).deliver_now
  end
end

# app/models/user.rb
class User < ApplicationRecord
  after_create do
    SendWelcomeEmailJob.perform_later(id)
    # Job only enqueued after the CREATE transaction commits
    # No risk of the job running before the user record exists
  end
end

Gdy opcja enqueue_after_transaction_commit jest aktywna, zadanie trafia do kolejki dopiero po zatwierdzeniu transakcji. Jeśli transakcja zostanie wycofana, zadanie nigdy nie zostaje dodane. Eliminuje to całą klasę wyścigów (race conditions), które regularnie dotykają systemy oparte na Redis.

Kontrola współbieżności i priorytetyzacja kolejek

Solid Queue oferuje wbudowane mechanizmy kontroli współbieżności — funkcjonalność, którą Sidekiq udostępnia wyłącznie w płatnej wersji Enterprise. Opcja limits_concurrency ogranicza liczbę jednocześnie uruchomionych zadań danego typu.

ruby
# app/jobs/api_sync_job.rb
class ApiSyncJob < ApplicationJob
  limits_concurrency to: 3, key: ->(account_id) { "api_sync_#{account_id}" }

  def perform(account_id)
    account = Account.find(account_id)
    ExternalApi.sync(account)
  end
end

Ten kod gwarantuje, że jednocześnie działają maksymalnie 3 zadania synchronizacji dla danego konta. Zablokowane zadania trafiają do tabeli solid_queue_blocked_executions i automatycznie zostają uruchomione, gdy zwolni się miejsce.

Priorytetyzacja działa na dwóch poziomach: numeryczny priorytet poszczególnych zadań (niższy numer oznacza wyższy priorytet) oraz kolejność kolejek w konfiguracji workera. Oba mechanizmy można łączyć, uzyskując precyzyjną kontrolę nad porządkiem wykonywania.

Wskazówka rekrutacyjna

Często zadawane pytanie na rozmowach dotyczących Rails 8 dotyczy porównania Solid Queue z Sidekiq. Kluczowa różnica: Solid Queue zapewnia gwarancje transakcyjne i wbudowaną kontrolę współbieżności bez dodatkowych kosztów. Sidekiq wygrywa pod względem surowej przepustowości przy dużych wolumenach (ponad 10 000 zadań na minutę), ponieważ Redis operuje w pamięci RAM. W przypadku 95% aplikacji wydajność Solid Queue jest w pełni wystarczająca.

Zadania cykliczne bez crona

Solid Queue zastępuje harmonogramy oparte na cronie wbudowanym schedulerem. Zadania cykliczne definiowane są w pliku konfiguracyjnym YAML i zarządzane przez dedykowany proces.

ruby
# config/recurring.yml
production:
  cleanup_expired_sessions:
    class: CleanupExpiredSessionsJob
    schedule: every 6 hours
  daily_report:
    class: DailyReportJob
    schedule: every day at 6am
    queue: reports
  weekly_digest:
    class: WeeklyDigestJob
    schedule: every Monday at 9am

Proces schedulera odczytuje ten plik i dodaje odpowiednie zadania do kolejki w zdefiniowanych interwałach. W odróżnieniu od crona, działa w ramach tego samego procesu nadzorcy (supervisor) i współdzieli połączenie z bazą danych.

Wdrożenie z integracją Puma

Rails 8 z Kamal oferuje bezproblemowe wdrożenie Solid Queue. Ustawienie zmiennej SOLID_QUEUE_IN_PUMA=1 informuje serwer Puma, aby uruchamiał i nadzorował procesy Solid Queue równolegle z serwerem webowym.

ruby
# config/puma.rb (Rails 8 default)
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]

Ten model wdrożenia w jednym procesie upraszcza orkiestrację kontenerów. Jeden obraz Docker obsługuje zarówno serwer webowy, jak i przetwarzanie zadań — idealne rozwiązanie dla małych i średnich aplikacji.

Gotowy na rozmowy o Ruby on Rails?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Solid Cache — buforowanie oparte na bazie danych, które skaluje się dzięki SSD

Solid Cache to implementacja ActiveSupport::Cache::Store oparta na bazie danych, która wykorzystuje wydajność nowoczesnych dysków SSD do zastąpienia Redis i Memcached. Założenie jest proste: dyski SSD oferują jedynie nieznacznie wolniejsze odczyty niż pamięć RAM, ale zapewniają wielokrotnie większą pojemność za ułamek ceny.

Solid Cache stosuje strategię usuwania FIFO (First In, First Out) zamiast LRU (Least Recently Used). Choć FIFO jest teoretycznie mniej efektywne, znacznie większa przestrzeń dyskowa kompensuje tę różnicę — wpisy pozostają w cache zdecydowanie dłużej, co w praktyce redukuje liczbę chybień.

ruby
# config/environments/production.rb
config.cache_store = :solid_cache_store

# config/solid_cache.yml
production:
  databases:
    - cache
  store_options:
    max_age: 604800        # 1 week in seconds
    max_size: 256           # Max entry size in KB
    namespace: "app_v1"
    expiry_batch_size: 100  # Entries cleaned per expiry cycle

Architektura cache i mechanizm wygasania wpisów

Solid Cache śledzi operacje zapisu za pomocą wewnętrznego licznika. Gdy licznik osiągnie 50% skonfigurowanej wartości expiry_batch_size, uruchamiane jest zadanie czyszczące przeterminowane wpisy. To podejście amortyzowane pozwala uniknąć przerw typu stop-the-world, które zdarzają się przy eksmisji LRU w instancjach Redis z ograniczoną pamięcią.

Tabela solid_cache_entries przechowuje wszystkie dane cache:

ruby
# db/cache_schema.rb (generated by installer)
create_table :solid_cache_entries do |t|
  t.binary :key, null: false, limit: 1024
  t.binary :value, null: false, limit: 262144  # 256 KB default
  t.datetime :created_at, null: false
  t.index :key, unique: true
  t.index :created_at  # For FIFO expiry
end

Domyślnie odczyt i zapis w tej tabeli korzystają z tej samej puli połączeń co reszta aplikacji. W przypadku aplikacji o dużym ruchu zaleca się skonfigurowanie osobnej bazy danych dla danych cache, aby operacje buforowania nie wpływały na wydajność głównej bazy.

Osobna baza danych dla cache w środowisku produkcyjnym

Izolacja Solid Cache na dedykowanej bazie danych jest prosta i zalecana dla obciążeń produkcyjnych generujących znaczny ruch cache.

yaml
# config/database.yml
production:
  primary:
    <<: *default
    database: myapp_production
  cache:
    <<: *default
    database: myapp_cache_production
    migrations_paths: db/cache_migrate

Taka separacja zapewnia, że miliony operacji zapisu cache nie zaśmiecają dziennika WAL (Write-Ahead Log) głównej bazy danych ani nie rywalizują o sloty w puli połączeń z zapytaniami aplikacyjnymi.

Uwaga dotycząca środowiska produkcyjnego

Bez osobnej bazy danych operacje odczytu i zapisu Solid Cache uczestniczą w każdej otaczającej transakcji ActiveRecord. Oznacza to, że długotrwała transakcja wstrzymuje zatwierdzenie wpisów cache. W środowisku produkcyjnym zawsze należy skonfigurować dedykowaną bazę danych dla cache.

Sharding i szyfrowanie danych cache

Solid Cache obsługuje rozkład danych cache na wiele baz danych (sharding) w celu rozłożenia obciążenia. Wspiera również szyfrowane atrybuty — dane w cache pozostają zaszyfrowane w spoczynku, co stanowi wymóg zgodności dla aplikacji przetwarzających dane wrażliwe.

ruby
# config/solid_cache.yml
production:
  databases:
    - cache_shard_1
    - cache_shard_2
    - cache_shard_3
  store_options:
    max_age: 1209600  # 2 weeks

Wpisy są rozdzielane pomiędzy shardy za pomocą spójnego hashowania (consistent hashing) na kluczu cache. Dodanie lub usunięcie sharda powoduje tymczasowy wzrost chybień w cache w trakcie redystrybucji, ale nie prowadzi do utraty danych.

Monitoring za pomocą Mission Control Jobs

Mission Control Jobs to panel webowy do inspekcji i zarządzania zadaniami Solid Queue. Wyświetla workery, kolejki oraz stany zadań (w trakcie, zakończone, zablokowane, nieudane) poprzez montowany silnik Rails.

ruby
# config/routes.rb
Rails.application.routes.draw do
  mount MissionControl::Jobs::Engine, at: "/jobs"
end

# Gemfile
gem "mission_control-jobs", ">= 1.0.1"

Mission Control udostępnia również API konsolowe do masowych operacji na nieudanych zadaniach:

ruby
# Rails console
ActiveJob.jobs.failed.where(job_class: "ApiSyncJob").retry_all
ActiveJob.jobs.failed.where(job_class: "BrokenJob").discard_all

Programatyczny dostęp do zadań jest niezbędny przy obsłudze awarii na dużą skalę, bez konieczności ręcznego klikania w interfejsie graficznym.

Najczęstsze pytania na rozmowach technicznych

Rozmowy kwalifikacyjne dotyczące Rails 8 coraz częściej koncentrują się na stosie Solid. Poniżej przedstawiono pytania, które pojawiają się najczęściej, wraz z oczekiwanym poziomem szczegółowości odpowiedzi.

P: W jaki sposób Solid Queue realizuje współbieżne przetwarzanie zadań bez Redis? Solid Queue wykorzystuje klauzulę FOR UPDATE SKIP LOCKED. Pozwala ona wielu workerom jednocześnie odpytywać tę samą tabelę bez wzajemnego blokowania. Każdy worker blokuje partię wierszy, przetwarza je i usuwa rekordy wykonań. Pozostałe workery pomijają zablokowane wiersze i rezerwują inne zadania.

P: Jaki problem rozwiązuje transakcyjne kolejkowanie? Zapobiega wykonywaniu zadań zanim transakcja bazodanowa, która je wyzwoliła, zostanie zatwierdzona. Bez tego mechanizmu zadanie mogłoby odwoływać się do rekordu, który został wycofany, powodując błąd ActiveRecord::RecordNotFound.

P: Dlaczego Solid Cache stosuje FIFO zamiast LRU? FIFO jest prostsze w implementacji w bazie danych i eliminuje narzut zapisu związany z aktualizacją znaczników czasu dostępu przy każdym odczycie. Kompensacją jest pojemność dysku SSD — wpisy żyją dłużej, więc współczynnik trafień pozostaje wysoki mimo mniej optymalnej strategii eksmisji.

P: Kiedy aplikacja powinna nadal korzystać z Redis zamiast stosu Solid? Redis pozostaje lepszym wyborem dla aplikacji przetwarzających ponad 10 000 zadań na minutę, obciążeń wymagających odczytów cache poniżej milisekundy lub aplikacji, które już używają Redis do innych celów (Pub/Sub, rate limiting, sesje). Stos Solid stawia na prostotę, nie na szczytową przepustowość.

Więcej pytań i ćwiczeń dostępnych jest w modułach ActiveJob i zadania w tle oraz Strategie buforowania na platformie SharpSkill.

Gotowy na rozmowy o Ruby on Rails?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Podsumowanie

  • Solid Queue zastępuje kolejki zadań oparte na Redis rozwiązaniem bazodanowym, wykorzystując FOR UPDATE SKIP LOCKED do współbieżnego przetwarzania
  • Transakcyjne kolejkowanie eliminuje wyścigi między zapisem do bazy a wykonaniem zadania — gwarancja, której Redis nie jest w stanie zapewnić
  • Wbudowana kontrola współbieżności, zadania cykliczne i integracja z Puma są dostępne w Solid Queue bez dodatkowych opłat
  • Solid Cache wykorzystuje buforowanie FIFO wspierane przez SSD z opcjonalnym shardingiem i szyfrowaniem, usuwając Memcached i Redis ze stosu infrastrukturalnego
  • Osobna konfiguracja bazy danych dla Solid Cache zapobiega wpływowi operacji cache na wydajność głównej bazy
  • Mission Control Jobs zapewnia monitoring produkcyjny oraz masowe zarządzanie zadaniami przez panel webowy i API konsolowe
  • Dla większości aplikacji Rails 8 w 2026 roku stos Solid jest zalecanym ustawieniem domyślnym — Redis pozostaje wyborem wyłącznie dla skrajnych przypadków wysokiej przepustowości przekraczającej 10 000 zadań na minutę

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Tagi

#ruby-on-rails
#solid-queue
#solid-cache
#rails-8
#rozmowa-techniczna

Udostępnij

Powiązane artykuły