Warum wir Redis verlassen und zu SolidQueue wechseln
(simplethread.com)- Rails 8 entfernt die Redis-Abhängigkeit aus dem Standard-Stack und stellt mit SolidQueue, SolidCache und SolidCable alle Aufgaben auf relationale Datenbanken (RDB) um
- Redis ist schnell und zuverlässig, verursacht aber operative Komplexität bei Konfiguration, Sicherheit, Cluster-Verwaltung und Backups
- SolidQueue nutzt die PostgreSQL-Funktion
FOR UPDATE SKIP LOCKED, um parallele Verarbeitung ohne Konkurrenz zu ermöglichen - Wiederkehrende Jobs, Concurrency Control, Monitoring-Dashboard (Mission Control) und andere kostenpflichtige Funktionen aus dem Redis+Sidekiq-Umfeld werden kostenlos bereitgestellt
- Für die meisten Rails-Anwendungen reicht SolidQueue aus; nur in einigen Fällen mit extrem hoher Geschwindigkeit oder Echtzeitverarbeitung sollte Redis beibehalten werden
Die versteckten Kosten von Redis
- Neben den reinen Hosting-Kosten bringt Redis laufenden Verwaltungsaufwand mit sich, etwa für Installation, Wartung, Sicherheitskonfiguration und HA-Cluster-Management
- Erforderlich sind Netzwerkverbindungen und Firewall-Konfiguration zwischen Rails und Redis, Client-Authentifizierung sowie die Orchestrierung von Sidekiq-Prozessen
- Im Fehlerfall müssen Redis und das RDBMS gleichzeitig debuggt werden; zusätzlich ist eine doppelte Backup-Strategie nötig
- Bei einem Rails-Stack ohne Redis muss dagegen nur ein einziges PostgreSQL-System verwaltet werden, was alles deutlich vereinfacht
So funktioniert SolidQueue
- Mit PostgreSQLs
FOR UPDATE SKIP LOCKEDholen sich mehrere Worker gleichzeitig Jobs, ohne Lock Contention zu erzeugen - Zentrale Tabellenstruktur
solid_queue_jobs: speichert Job-Metadatensolid_queue_scheduled_executions: wartet auf geplante Jobssolid_queue_ready_executions: Queue für ausführungsbereite Jobs
- Worker-, Dispatcher-, Scheduler- und Supervisor-Prozesse pollen in regelmäßigen Abständen unterschiedliche Tabellen und arbeiten zusammen
- Dank PostgreSQLs MVCC-Architektur und autovacuum lassen sich auch große Mengen an Insert- und Delete-Operationen stabil verarbeiten
Planung wiederkehrender Aufgaben
- SolidQueue bietet cron-artige wiederkehrende Jobs standardmäßig und wird über die Datei
config/recurring.ymlkonfiguriert - Der Scheduler legt fällige Jobs in die Queue und plant den nächsten Ausführungszeitpunkt automatisch
- Die Bibliothek Fugit parst natürliche Zeitpläne, und Concurrent::ScheduledTask erzeugt die Threads
- Übernommen wurde der deterministische Scheduling-Ansatz von GoodJob, sodass Zeitpläne auch nach Prozessneustarts erhalten bleiben
Funktionen zur Concurrency Control
- SolidQueue unterstützt mit einem POSIX-Semaphor-Muster die Begrenzung gleichzeitiger Ausführungen pro Job-Einheit
- Beispiel: Mit
limits_concurrency to: 1, key: ->(user) { user.id }wird pro Benutzer nur ein Job gleichzeitig ausgeführt
- Beispiel: Mit
- Durch das Setzen einer Semaphore-Ablaufzeit (
duration) lassen sich Job-Konflikte und Deadlocks vermeiden - Zugehörige Tabellen
solid_queue_semaphores: verfolgt Concurrency-Limitssolid_queue_blocked_executions: speichert wartende Jobs
Monitoring mit Mission Control
- Mission Control Jobs ist ein kostenloses Open-Source-Dashboard für Rails 8 und lässt sich einfach unter dem Pfad
/jobsmounten - Wichtige Funktionen
- Echtzeitstatus der Queues, Nachverfolgung fehlgeschlagener Jobs sowie Steuerung von Retry/Discard
- Visualisierung von Zeitachsen für geplante und wiederkehrende Jobs
- Durchsatz- und Metrikdiagramme pro Queue
- Da Abfragen auf SQL basieren, ist direkte Analyse in der Datenbank ohne zusätzliche Tools möglich
Migration von Sidekiq zu SolidQueue
- Schritt 1:
config.active_job.queue_adapter = :solid_queuesetzen - Schritt 2:
bundle add solid_queueausführen, danachrails solid_queue:installunddb:migrate - Schritt 3: Cron-Schedules aus
sidekiq.ymlnachrecurring.ymlübertragen - Schritt 4: In der
Procfilejobs: bundle exec rake solid_queue:startergänzen - Schritt 5: Redis- und Sidekiq-bezogene Gems entfernen
- Vorhandener ActiveJob-Code funktioniert unverändert weiter
Wann Redis weiterhin nötig ist
- Dauerhafte Verarbeitung von mehreren tausend Jobs pro Sekunde
- Echtzeitsysteme, bei denen Latenz unter 1 ms zwingend erforderlich ist
- Bedarf an komplexen Pub/Sub-Strukturen oder ausgefeiltem Rate Limiting bzw. Counter-Operationen
- Als Beispiel betreibt Shopify 833 Requests pro Sekunde und 1.172 Worker-Prozesse und nutzt dafür eine Redis-Infrastruktur
Leitfaden für die praktische Implementierung
- Beim Erstellen einer neuen Rails-8-App werden SolidQueue, SolidCache und SolidCable automatisch konfiguriert
- In
config/database.ymlwird eine separate Queue-Datenbankverbindung empfohlen - Authentifizierung für Mission Control hinzufügen und die Route
/jobsmounten Procfile.devumjobs: bundle exec rake solid_queue:startergänzen und dann alles mitbin/devstarten- Nach dem Erzeugen von Test-Jobs lässt sich ihr Status in Mission Control prüfen
Häufige Probleme und Lösungen
- Auch eine Single-Database-Konfiguration ist möglich, reduziert aber die betriebliche Flexibilität
- In Mission Control in Produktion sollte unbedingt Authentifizierung aktiviert werden
- Die Standardwerte für das Polling-Intervall – 1 Sekunde für geplante Jobs, 0,2 Sekunden für sofortige Jobs – passen für die meisten Anwendungen
- Bei Nutzung von ActionCable/Turbo Streams sollte
SolidCablemit einer separaten Datenbankverbindung konfiguriert werden
Skalierbarkeit und Performance
- SolidQueue skaliert für die meisten Rails-Anwendungen ausreichend
- Auf PostgreSQL-Basis sind 200 bis 300 Jobs pro Sekunde möglich, und 37signals verarbeitet 20 Millionen Jobs pro Tag ohne Redis
- Vergleichstabelle
Punkt Redis + Sidekiq SolidQueue Konfigurationsaufwand Separater Service nötig Integrierte DB Abfragesprache Redis-Befehle SQL Monitoring Separates Dashboard Mission Control Fehlerszenarien 6 oder mehr 2 Durchsatz Tausende Jobs/Sek. 200–300 Jobs/Sek. Geeignet für 99,9 % Apps 95 % Apps
Fazit
- Redis und Sidekiq sind hervorragende Technologien, verursachen aber für die meisten Rails-Anwendungen unnötige Komplexität und zusätzliche Kosten
- SolidQueue ermöglicht auf Basis einer einzigen Datenbank einfacheren Betrieb, geringere Kosten und effizientere Wartung
- Für das Rails-8-Zeitalter wird der Umstieg auf SolidQueue als Standardwahl empfohlen
2 Kommentare
Redis ist gut.
Hacker-News-Kommentare
Ich finde, jeder Open-Source-Autor hat das Recht, den Umfang seines Projekts zu kontrollieren
Allerdings bereuen wir im Team, von good_job auf SolidQueue umgestiegen zu sein
Basecamp ist MySQL-zentriert und akzeptiert daher keine RDBMS-Engine-spezifischen Queries. In den GitHub-Issues sieht man, dass der Fokus fast nur auf der MySQL-Performance liegt
Außerdem gibt es noch keine Unterstützung für Batch-Jobs (zugehöriger PR)
Bei komplexen JOINs erstellt MySQL oft schlechte Query-Pläne, deshalb erzwinge ich die Reihenfolge mit STRAIGHT_JOIN. Das ist Zukunftsvorsorge
Ich vergleiche beide gerade als Kandidaten für einen Umstieg von resque. GoodJob ist wegen seiner pg-spezifischen Features nicht mit dem Transaction-Modus von pgbouncer kompatibel
Dass man persistente Sessions braucht, ist lästig, aber der Performance-Gewinn ist in den meisten Größenordnungen nicht besonders relevant
Trotzdem wirken das Entwicklungsmodell und die Lesbarkeit des Codes von GoodJob deutlich vertrauenswürdiger
Wenn die Produktionsumgebung einfacher werden kann, ist das immer gut
Ich denke, im Idealfall sollte Rails eine Architektur bieten, die einen einfachen Wechsel zu Redis erlaubt
Es wäre gut, mit SolidQueue zu starten und bei Skalierungsgrenzen später auf Redis wechseln zu können
Die meisten Rails-Apps haben keinen hohen Traffic, daher ist der Betrieb beider Systeme eher zusätzliche Komplexität
Natürlich gibt es Apps, die von einer bestimmten Queue-Implementierung abhängen, aber normalerweise reicht es, die Konfiguration zu ändern
Ich würde gern wissen, ob zusätzlich Snapshots verwendet werden, um zu große Logs zu vermeiden, und ob das auch im verteilten Modus funktioniert
Vor allem wenn Jobs zusammen mit anderen DB-Änderungen erzeugt werden, ist der Verlust dieser Garantien problematisch
Redis war hier im Vorteil, weil es ein leichtgewichtiges, unabhängiges Storage-System ist
SolidQueue scheint diese Trennung nicht klar zu machen (riverqueue.com)
Ich habe SolidQueue in meinem Side-Project ausprobiert
Mein Fazit: Wenn es mit Sidekiq kein Problem gibt, gibt es keinen Grund für einen Wechsel
In Betracht ziehen würde ich es nur, wenn man die Redis-Infrastruktur loswerden will
Für neue Projekte wirkt GoodJob reifer und hat auch die bessere Community
Die UI von SolidQueue ist zu schlicht, das war unpraktisch. Ohne Index-Optimierung hing die Seite bei größeren Datenmengen
Man sollte auch bedenken, dass mit einem RDBMS zusätzlicher Aufwand für das Connection-Pool-Management entsteht
Für alle, die sich Sorgen um die Skalierung machen: Der Benchmark von Elixirs Oban zeigt
auf einem einzelnen Node die Verarbeitung von einer Million Jobs pro Minute. Die meisten Apps liegen weit darunter
Dort werden 5000 Jobs auf einmal als Batch eingefügt, die TPS liegt also real nur bei etwa 200
Fügt man einzelne Jobs ohne Batch ein, ist die Belastung durch SQL-Transaktionen deutlich höher
Wir haben schon vor SolidQueue Jobs in der DB gespeichert
Der Vorteil ist, dass man den Produktionszustand als Snapshot direkt in die Entwicklungsumgebung übernehmen kann
Den Rate Limiter lassen wir aber in Redis, um die DB-Last zu begrenzen
Die Grenze DB-basierter Queues sind große Payloads
Wenn man große JSON-Daten in die Queue legt, ist das wegen des DB-Schreib-Overheads ineffizient
Redis (Sidekiq) ist in solchen Fällen deutlich schneller
SolidQueue+SQLite ist als reine Übergabe des Primary Keys noch okay
Aber wenn viele Worker dieselbe DB pollen, wird das schnell zum Bottleneck
Ich denke, große Daten sollten in externem Storage wie S3 liegen und nur per Referenz übergeben werden
Mich würde interessieren, ob es dazu irgendwo zusammengefasste Benchmark-Ergebnisse gibt
In SolidQueue wird SKIP LOCKED erwähnt, aber einen 15-Minuten-Job in einer Transaktion offen zu halten, ist riskant
Lang laufende offene Transaktionen ruinieren die DB-Performance und sind auch anfällig für Netzwerkabbrüche
So ein Aufbau kann in ein Antipattern führen. Später habe ich gesehen, dass es wohl lease-basiert ist
Ich kann mich mit der Philosophie Postgres for everything identifizieren
Ich halte es für gut, die Dinge einfach zu halten und alles in PostgreSQL zu konsolidieren
Ich weiß nicht recht, wie ich auf dieses Bild entgegnen soll
Ich frage mich, ob Redis die zusätzliche Komplexität überhaupt noch wert ist
„Ein Business, bei dem Latenzen unter 1 ms wichtig sind“ — soll das heißen, man macht HFT mit Rails?
Postgres wird die Welt verschlingen