- Rails 8, varsayılan yığından Redis bağımlılığını kaldırıyor ve SolidQueue·SolidCache·SolidCable ile tüm işleri ilişkisel veritabanı (RDB) üzerinde çalıştırmaya geçiyor
- Redis hızlı ve güvenilir olsa da kurulum, güvenlik, küme yönetimi, yedekleme gibi operasyonel karmaşıklıklar doğurur
- SolidQueue, PostgreSQL’in
FOR UPDATE SKIP LOCKED özelliğini kullanarak çekişmesiz paralel işleme gerçekleştirir
- Periyodik işler, eşzamanlılık kontrolü, izleme panosu (Mission Control) gibi Redis+Sidekiq ücretli özelliklerini ücretsiz sunar
- Çoğu Rails uygulaması için SolidQueue yeterlidir; yalnızca çok yüksek hızda, gerçek zamanlı işleme gereken bazı durumlarda Redis’i korumak gerekir
Redis’in gizli maliyeti
- Redis, yalnızca barındırma maliyetinin ötesinde kurulum, bakım, güvenlik yapılandırması, HA küme yönetimi gibi sürekli bir yönetim yükü getirir
- Rails ile Redis arasında ağ bağlantısı ve güvenlik duvarı ayarları, istemci kimlik doğrulaması, Sidekiq süreç orkestrasyonu gerekir
- Arıza durumunda Redis ve RDBMS olmak üzere iki sistemi aynı anda hata ayıklamak gerekir; ayrıca çift yedekleme stratejisi de gerekir
- Buna karşılık Redis’siz bir Rails yığınında yalnızca PostgreSQL’i yönetmek yeterlidir, bu da yapıyı sadeleştirir
SolidQueue nasıl çalışır
- PostgreSQL’in
FOR UPDATE SKIP LOCKED özelliği kullanılarak birden fazla worker, kilit çekişmesi (lock contention) olmadan işleri aynı anda alır
- Temel tablo yapısı
solid_queue_jobs: iş meta verilerini saklar
solid_queue_scheduled_executions: zamanlanmış işleri bekletir
solid_queue_ready_executions: çalıştırılmaya hazır iş kuyruğu
- worker, dispatcher, scheduler, supervisor süreçleri farklı tabloları periyodik olarak poll ederek birlikte çalışır
- PostgreSQL’in MVCC tasarımı ve autovacuum sayesinde büyük hacimli ekleme/silme işlemleri de kararlı biçimde işlenir
Tekrarlayan işlerin zamanlanması
- SolidQueue, cron tarzı tekrarlayan işleri yerleşik olarak sunar ve
config/recurring.yml dosyasıyla yapılandırılır
- Scheduler, çalışma zamanı gelen işleri kuyruğa ekler ve bir sonraki çalıştırma zamanını otomatik olarak planlar
- Fugit kütüphanesiyle doğal dildeki zamanlamalar ayrıştırılır, Concurrent::ScheduledTask ile thread oluşturulur
- GoodJob’ın deterministik zamanlama yaklaşımı benimsenerek süreç yeniden başlatılsa bile takvimin korunması sağlanır
Eşzamanlılık kontrolü özelliği
- SolidQueue, POSIX semaphore deseni kullanarak iş bazında eşzamanlı çalıştırma sınırı desteği sunar
- Örnek:
limits_concurrency to: 1, key: ->(user) { user.id } ayarıyla kullanıcı başına yalnızca 1 iş çalıştırılır
- Semaphore sona erme süresi (
duration) belirlenerek iş çakışmaları ve deadlock önlenir
- İlgili tablolar
solid_queue_semaphores: eşzamanlılık sınırını izler
solid_queue_blocked_executions: bekleyen işleri saklar
Mission Control ile izleme
- Mission Control Jobs, Rails 8 için ücretsiz açık kaynaklı bir panodur ve
/jobs yoluna kolayca mount edilebilir
- Başlıca özellikler
- Gerçek zamanlı kuyruk durumu, başarısız işler takibi, yeniden deneme/iptal kontrolü
- Zamanlanmış ve tekrarlayan işler için zaman çizelgesi görselleştirmesi
- Kuyruk bazında throughput ve metrik grafikleri
- SQL tabanlı sorgulamayı desteklediği için ek araç olmadan doğrudan veritabanında analiz yapılabilir
Sidekiq’ten SolidQueue’ya geçiş
- 1. adım:
config.active_job.queue_adapter = :solid_queue ayarını yapın
- 2. adım:
bundle add solid_queue ardından rails solid_queue:install ve db:migrate çalıştırın
- 3. adım:
sidekiq.yml içindeki cron zamanlamalarını recurring.yml biçimine dönüştürün
- 4. adım:
Procfile içine jobs: bundle exec rake solid_queue:start ekleyin
- 5. adım: Redis ve Sidekiq ile ilgili gem’leri kaldırın
- Mevcut ActiveJob kodu değişiklik olmadan çalışmaya devam eder
Redis’in hâlâ gerekli olduğu durumlar
- Saniyede binlerce işi aşan sürekli işleme yükü
- 1 ms altı gecikme (latency) zorunlu olan gerçek zamanlı sistemler
- Karmaşık pub/sub yapıları veya gelişmiş rate limiting ve sayaç işlemleri gereksinimi
- Örnek olarak Shopify, saniyede 833 istek ve 1.172 worker süreci çalıştırırken Redis altyapısı kullanıyor
Pratik uygulama rehberi
- Yeni bir Rails 8 uygulaması oluşturulduğunda SolidQueue, SolidCache ve SolidCable otomatik yapılandırılır
config/database.yml içinde ayrı bir queue veritabanı bağlantısı tanımlanması önerilir
- Mission Control kimlik doğrulamasını ekleyin ve
/jobs rotasını mount edin
Procfile.dev içine jobs: bundle exec rake solid_queue:start ekleyip bin/dev çalıştırarak tüm sistemi başlatın
- Test işi oluşturduktan sonra durumu Mission Control üzerinden kontrol edebilirsiniz
Sık görülen sorunlar ve çözümleri
- Tek veritabanı yapılandırması da mümkündür, ancak operasyonel esnekliği azaltır
- Production ortamındaki Mission Control için mutlaka kimlik doğrulama eklenmelidir
- Polling aralığı varsayılan olarak zamanlanmış işler için 1 saniye, anlık işler için 0,2 saniyedir ve çoğu uygulama için uygundur
- ActionCable/Turbo Streams kullanılıyorsa
SolidCable ayrı bir veritabanı bağlantısıyla yapılandırılmalıdır
Ölçeklenebilirlik ve performans
- SolidQueue, çoğu Rails uygulaması için yeterince ölçeklenebilir
- PostgreSQL tabanıyla saniyede 200–300 iş işleyebilir ve 37signals, Redis olmadan günde 20 milyon işi işler
- Karşılaştırma tablosu
| Öğe |
Redis + Sidekiq |
SolidQueue |
| Yapılandırma karmaşıklığı |
Ayrı servis gerekir |
Yerleşik DB kullanır |
| Sorgu dili |
Redis komutları |
SQL |
| İzleme |
Ayrı pano |
Mission Control |
| Arıza senaryosu |
6+ |
2 |
| Throughput |
Binlerce iş/sn |
200–300 iş/sn |
| Uygun olduğu alan |
Uygulamaların %99,9’u |
Uygulamaların %95’i |
Sonuç
- Redis ve Sidekiq güçlü teknolojiler olsa da çoğu Rails uygulaması için gereğinden fazla karmaşıklık ve maliyet yaratır
- SolidQueue, tek veritabanı temeliyle operasyonel sadeleşme, maliyet düşüşü ve bakım verimliliği sağlar
- Rails 8 döneminde varsayılan tercih olarak SolidQueue’ya geçiş önerilir
2 yorum
Redis güzel ama.
Hacker News görüşleri
Her açık kaynak yazarının kendi projesinin kapsamını kontrol etme hakkı olduğunu düşünüyorum
Ama ekibimizin good_job'dan SolidQueue'ya geçmesi konusunda pişmanız
Basecamp MySQL merkezli olduğu için RDBMS motoruna özgü sorguları kabul etmiyor. GitHub issue'larına bakınca sadece MySQL performansına odaklandıkları görülüyor
Ayrıca hâlâ batch job desteği yok (ilgili PR)
Karmaşık JOIN'lerde MySQL sık sık sorgu planını yanlış kuruyor, bu yüzden ben STRAIGHT_JOIN ile sırayı zorluyorum. Geleceğe dönük bir önlem
Ben de resque'ten geçiş için bu ikisini karşılaştırıyorum. GoodJob, pg'ye özgü özellikleri yüzünden pgbouncer transaction moduyla uyumlu değil
Session kalıcılığı gerektiği için uğraştırıcı ama performans artışı çoğu ölçekte çok da anlamlı değil
Yine de GoodJob'ın geliştirme modeli ve kod okunabilirliği çok daha fazla güven veriyor
Prodüksiyon ortamı sadeleşebiliyorsa bu her zaman iyidir
Rails'te ideal durumun, Redis'e kolayca geçilebilen bir yapı olduğunu düşünüyorum
SolidQueue ile başlayıp ölçek sınırına takılınca Redis'e geçebilmek güzel olurdu
Çoğu Rails uygulamasının trafiği çok büyük değil, bu yüzden iki sistemi birden sürdürmek daha karmaşık
Elbette belirli bir kuyruk uygulamasına bağımlı olan uygulamalar da var ama genel durumda sadece ayarı değiştirmek yeterli
Log'un fazla büyümesini önlemek için snapshot'la birlikte kullanılıp kullanılmadığını ve bunun dağıtık modda da çalışıp çalışmadığını öğrenmek isterim
Özellikle iş oluşturma işlemi başka DB değişiklikleriyle birlikte oluyorsa, bu garantiyi kaybetmek sorun olur
Redis bu noktada hafif ve bağımsız bir state store olarak avantajlıydı
SolidQueue sanki bu ayrımı netleştirmiyor (riverqueue.com)
Side project'imde SolidQueue'yu denedim
Sonuç olarak, Sidekiq ile ilgili bir sorun yoksa geçmek için güçlü bir neden yok
Ancak Redis altyapısından kurtulmak istiyorsanız düşünmeye değer
Yeni bir projede olsam GoodJob tarafı bana daha olgun ve topluluk açısından daha güçlü gelir
SolidQueue'nun UI'ı fazla basit olduğu için kullanışsızdı. İndeks optimizasyonu yoktu; veri artınca sayfa donuyordu
RDBMS kullanınca connection pool yönetim maliyetinin de eklendiğini hesaba katmak gerekir
Ölçeklenebilirlik konusunda endişelenenler için, Elixir'nin Oban benchmark'ına bakarsanız
tek bir node'da dakikada bir milyon iş işliyor. Çoğu uygulamanın iş yükü bunun çok altında
Yapı, 5000 işi aynı anda batch olarak ekliyor; dolayısıyla TPS gerçekte yaklaşık 200
Batch olmadan işleri tek tek eklerseniz SQL transaction yükü çok daha fazla olur
Biz SolidQueue'dan önce de işleri DB'de saklıyorduk
Avantajı, prodüksiyon durumunu aynen geliştirme ortamına snapshot alabilmek
Ama rate limiter'ı Redis'te tutuyoruz. DB yükünü önlemek için
DB tabanlı kuyrukların sınırı büyük payload'lardır
Büyük JSON'u kuyruğa koyarsanız DB yazma overhead'i yüzünden verimsiz olur
Redis (Sidekiq) bu durumda çok daha hızlı
SolidQueue+SQLite, sadece primary key iletmek için fena değil
Ama çok sayıda worker aynı DB'yi polling yaparsa hızla bottleneck oluşur
Büyük veriyi S3 gibi harici storage'da tutup sadece referans geçirmek daha iyi olur diye düşünüyorum
Acaba benchmark sonuçlarını derleyen bir kaynak var mı?
SolidQueue'da SKIP LOCKED'dan bahsediliyor ama 15 dakikalık bir işi transaction boyunca açık tutmak riskli
Uzun süre açık kalan transaction'lar DB performansını bozar ve ağ kopmalarına karşı da kırılgandır
Böyle bir yapı anti-pattern'lere yol açabilir. Sonradan bakınca lease yaklaşımı gibi görünüyor
Postgres for everything felsefesine katılıyorum
Basitçe her şeyi PostgreSQL'de birleştirmek iyi bir fikir gibi geliyor
Bu benzetmeye nasıl karşı çıkacağımı bilmiyorum
Karmaşıklığı artırma pahasına Redis kullanmaya gerçekten değer mi emin değilim
“1 ms altı latency'nin önemli olduğu bir iş” deniyor; yani Rails ile HFT mi yapılıyor?
Postgres dünyayı yiyecek