Skip to content
← Wszystkie wpisy
7 min czytania Michał Smykowski

Zapowiedź Rails 8: Solid Queue, Solid Cache i koniec z Redisem

Rails 8 nadchodzi z po cichu radykalną ideą: być może już nie potrzebujesz Redisa. Solid Queue i Solid Cache przenoszą zadania w tle i cache do bazy, zwijając standardowy produkcyjny stack Railsów. Spojrzenie na to, co robią biblioteki Solid, dlaczego „po prostu użyj Postgresa” wciąż wygrywa i kiedy stara odpowiedź jeszcze się broni.

Przez większą część ostatniej dekady produkcyjna aplikacja Railsów miała standardowy zestaw wspierający: Postgres jako bazę i Redisa do rzeczy, których Postgres rzekomo nie robił wystarczająco dobrze — kolejki zadań w tle (Sidekiq), cache i tak dalej. „Railsy plus Postgres plus Redis” stało się na tyle odruchowe, że mało kto to jeszcze kwestionował. Rails 8, które wkrótce wejdzie do gry, przynosi po cichu radykalną propozycję: być może już nie potrzebujesz Redisa. Rodzina bibliotek pod wspólną nazwą „Solid” — Solid Queue do zadań w tle i Solid Cache do cache’owania — przenosi te obciążenia z powrotem do twojej bazy, a domyślne ustawienia nowych aplikacji są wokół nich zbudowane. To zapowiedź tego, co biblioteki Solid faktycznie robią, dlaczego instynkt „po prostu użyj Postgresa” wciąż wygrywa i uczciwej odpowiedzi na pytanie, kiedy stara odpowiedź spod znaku Redisa jeszcze się broni.

Stack, który Rails 8 próbuje zwinąć

Żeby zobaczyć, co się zmienia, trzeba nazwać to, co się zmienia. Konwencjonalny produkcyjny stack Railsów ma co najmniej trzy stanowe elementy:

  • Postgres — główna baza, w której trzymasz dane aplikacji.
  • Redis — magazyn danych w pamięci, wykorzystywany jako backend kolejek zadań w tle (najczęściej przez Sidekiqa), do cache’owania i często do innego efemerycznego stanu.
  • Oraz operacyjny ciężar utrzymywania, monitorowania, zabezpieczania i backupowania obu tych niezależnych systemów.

Redis zasłużył na swoje miejsce: jest szybki, a dekadę temu opcje kolejek i cache’a oparte na bazie były po prostu gorsze. Ale każdy dodatkowy kawałek stanowej infrastruktury to stały podatek — kolejna rzecz do zaprowizjonowania, monitorowania, zabezpieczenia, aktualizowania i ogarniania głową, gdy coś się popsuje. Rails 8 stawia na to, że w bardzo wielu aplikacjach ten drugi magazyn danych po prostu nie jest już wart tego ciężaru, bo baza potrafi obsłużyć te zadania wystarczająco dobrze. Biblioteki Solid są sposobem, w jaki Rails na tym zakładzie wychodzi na swoje.

Solid Queue: zadania w tle w bazie

Solid Queue to backend Active Job oparty na bazie — kolejka zadań, która trzyma swoje zadania w twojej relacyjnej bazie zamiast w Redisie. Zadania kolejkujesz dokładnie tak jak dotychczas, przez Active Job; różnica polega wyłącznie na tym, gdzie one leżą i jak są przetwarzane. Zadania siedzą w tabelach bazy, a procesy workerów Solid Queue je pobierają i uruchamiają.

Argument za tym jest ten sam, który przewija się w tej serii: prostota operacyjna przez konsolidację. W Solid Queue twoje zadania żyją w tej samej bazie co dane aplikacji, co daje konkretne rzeczy:

  • Jeden magazyn danych zamiast dwóch. Żadnego osobnego Redisa do uruchamiania, monitorowania, zabezpieczania i backupowania wyłącznie na potrzeby kolejki. Dla małego zespołu wykreślenie całego kawałka infrastruktury to znacząca redukcja powierzchni operacyjnej.
  • Transakcyjne kolejkowanie. Ponieważ zadania są w twojej bazie, możesz zakolejkować zadanie w tej samej transakcji, co zmiana danych, która je wywołuje. Albo obie operacje się commitują, albo żadna — co elegancko eliminuje klasyczny błąd, w którym dane się zapisują, ale wrzucenie do Redisa się nie udaje (albo odwrotnie), zostawiając dwa magazyny w rozjechanym stanie. Gdy kolejka jest w bazie, całe to okno niespójności się zamyka.
  • Te same backupy i te same narzędzia. Zadania są backupowane razem z resztą bazy, można je podejrzeć narzędziami SQL-owymi, których już używasz. Nie ma drugiego systemu z osobnym modelem operacyjnym.

Solid Queue jest zaprojektowana tak, żeby udźwignąć spore wolumeny zadań, a nowoczesny Postgres bez problemu potrafi być backendem kolejki dla zdecydowanej większości aplikacji. Sidekiq na Redisie wciąż jest szybszy przy ekstremalnej przepustowości — ale „ekstremalnej” to tu słowo kluczowe, a większość aplikacji nawet się do tego pułapu nie zbliża.

Solid Cache: cache w bazie

Solid Cache stosuje dokładnie tę samą ideę do cache’owania: to magazyn cache oparty na bazie. Na pierwszy rzut oka brzmi to wbrew logice — czy nie w tym rzecz z cache’em, że jest szybszy od bazy, przed którą go stawiasz? Cały fortel polega na tym, że Solid Cache jest zaprojektowana pod przechowywanie na dysku i pod nowoczesny sprzęt, a dla wielu zastosowań cache’owania baza — mądrze użyta — jest wystarczająco szybka. Za to znacznie większa pojemność magazynu na dysku oznacza, że możesz cache’ować dużo więcej, niż zmieści się w cache’u trzymanym w pamięci, co często poprawia współczynnik trafień na tyle, żeby zrównoważyć różnicę w koszcie pojedynczego odczytu. To po prostu inny punkt na krzywej prędkość–pojemność i dla wielu aplikacji punkt lepszy — a przy okazji o jeden system mniej do utrzymania.

Dlaczego „po prostu użyj Postgresa” wciąż wygrywa

W ostatnim kierunku, w którym idą Railsy, widać wyraźną nić przewodnią i warto ją nazwać, bo to faktyczna filozofia, a nie tylko funkcja: wybieraj mniej ruchomych elementów; sięgaj po bazę, zanim sięgniesz po kolejny system. To ten sam instynkt, który stoi za hasłem „Postgres do wszystkiego”, wracającym w niejednym z moich tekstów — obsługuj kolejki, cache, wyszukiwanie pełnotekstowe i resztę w bazie, którą i tak już masz, zamiast dokładać wyspecjalizowaną infrastrukturę do każdej z tych rzeczy. Zysk jest operacyjny: mniej magazynów danych to mniej do utrzymania, mniej rzeczy, które mogą się zepsuć, mniej granic spójności do skoordynowania — i mniejszy zespół, który jest w stanie to wszystko udźwignąć. Dla etosu frameworka dla jednej osoby, w stronę którego Railsy się kierują, wyrzucenie Redisa z domyślnego stacka jest podręcznikowym ruchem w linii z tą filozofią — o jedną rzecz mniej między deweloperem a wdrożoną, łatwą do utrzymania aplikacją.

Warto podkreślić, że to domyślne się przesuwa, a nie jakaś możliwość zostaje odebrana. Rails 8 robi z opcji opartych na bazie ścieżkę out-of-the-box, tak że nowa aplikacja potrzebuje tylko Postgresa do obsługi kolejek i cache — ale masz pełną swobodę używania Redisa i Sidekiqa, a w części aplikacji nadal powinieneś.

Kiedy stara odpowiedź jeszcze się broni

Zgodnie z każdym uczciwym „czy powinieneś to wdrożyć” w tej serii: biblioteki Solid to właściwe domyślne, a nie uniwersalny nakaz. Redis wciąż wygrywa w konkretnych przypadkach i powinieneś po niego sięgać świadomie, kiedy masz realny powód:

  • Ekstremalna przepustowość zadań. Jeśli faktycznie mielisz bardzo duże wolumeny zadań i szybkość in-memory Redisa robi wymierną różnicę, Sidekiq na Redisie pozostaje mocniejszym wyborem. Ale najpierw zmierz — większość aplikacji, które zakładają, że tego potrzebują, w rzeczywistości nie potrzebuje.
  • Cache, który naprawdę wymaga latencji in-memory. Jeśli masz gorącą ścieżkę cache’owania, w której różnica między odczytem z pamięci a z dysku faktycznie widać w metrykach, cache w pamięci jest właściwym wyborem. Znowu: mierz, nie zakładaj.
  • Redisa i tak dobrze u siebie prowadzisz z innych powodów. Jeśli Redis siedzi w twoim stacku, obsługując rzeczy, których biblioteki Solid nie pokrywają, argument z konsolidacji jest słabszy.

Decyzja to znajome dopasuj-narzędzie-do-problemu. Domyślnie sięgaj po biblioteki Solid i korzystaj ze zwiniętego stacka; zachowaj Redisa, gdy zmierzony wymóg uzasadnia dodatkową infrastrukturę. Czego nie warto robić, to odruchowo dokładać Redisa do nowej aplikacji Rails 8 z przyzwyczajenia, płacąc za drugi magazyn danych, którego już nie potrzebujesz.

Werdykt

Po cichu radykalny pomysł Rails 8 to ten, że odruchowy stack „Railsy plus Postgres plus Redis” może się zwinąć: Solid Queue przenosi zadania w tle do bazy, Solid Cache przenosi tam też cache, a domyślne ustawienia nowych aplikacji są wokół nich zbudowane tak, że świeżej aplikacji Railsów wystarczy sam Postgres. Zyskiem jest ta operacyjna prostota, do której ta seria wciąż wraca — jeden stanowy system zamiast dwóch, transakcyjne kolejkowanie zamykające klasyczną lukę spójności, te same backupy i narzędzia dla wszystkiego oraz stack, który mały zespół faktycznie jest w stanie ogarnąć. To filozofia „po prostu użyj Postgresa / framework dla jednej osoby” zamieniona w domyślne ustawienie i dla zdecydowanej większości aplikacji jest to właściwe domyślne: nowoczesny Postgres spokojnie udźwignie kolejki i cache przy wolumenach, z którymi większość aplikacji rzeczywiście się mierzy. Stara odpowiedź spod znaku Redisa nadal się broni w konkretnych, zmierzonych przypadkach — ekstremalna przepustowość zadań, ścieżka cache’a faktycznie wymagająca latencji in-memory albo Redis, który i tak dobrze prowadzisz — i tam warto przy nim zostać. Ale odruch dokładania Redisa do każdej aplikacji Railsów ma już swoje najlepsze lata za sobą. Domyślnie idź w stack Solid, skasuj magazyn danych, którego już nie potrzebujesz, a po Redisa sięgaj tylko wtedy, gdy stawia go z powrotem na stole realny wymóg, a nie przyzwyczajenie.