- Node.js/TypeScript tabanlı bir backend'de büyük ölçekli gerçek zamanlı güncellemeleri işlemek gereken bir durum söz konusuydu
- Backend olarak PostgreSQL kullanılıyordu; yüzlerce worker node'un yeni işleri sürekli kontrol etmesi ve agent'ların çalıştırma ile sohbet durumu güncellemelerini alması gerekiyordu
- Araştırma WebSocket ile başladı, ancak şaşırtıcı derecede etkili bir "eski usul" çözüme ulaştı
→ "Postgres ile HTTP Long Polling"
Sorun: büyük ölçekli gerçek zamanlı güncellemeler
- Worker node güncellemeleri :
- Node.js/Golang/C# SDK'larını çalıştıran yüzlerce worker node vardı
- Yeni bir iş gelir gelmez bunu öğrenmeleri gerekiyordu; bu yüzden Postgres veritabanını zorlamayacak bir sorgu stratejisine ihtiyaç vardı
- Agent durum senkronizasyonu :
- Agent'ların çalıştırma ve sohbet durumu için gerçek zamanlı güncellemelere ihtiyacı vardı ve bunların verimli şekilde akış olarak iletilmesi gerekiyordu
Long polling ile WebSocket karşılaştırması
- Short polling, yolcu olup olmadığına bakmadan belirli aralıklarla hareket eden, tarifeye sıkı sıkıya bağlı bir tren gibidir
- Long polling'de sunucu yanıt vermek için bekler; veri oluştuğunda hemen döner, belirli bir süre geçerse timeout ile yanıt verir
- Yani, "bekleyip veri gelince hareket eden" bir tren gibidir. Yalnızca belirli bir süre (TTL) içinde yolcu çıkmazsa boş olarak hareket eder
- Veri (yolcu) varsa hemen hareket etme ve yoksa kaynakları verimli kullanma avantajlarını birlikte sunar
- WebSocket, bağlantının sürekli açık tutulduğu ve verinin çift yönlü aktarıldığı bir yöntemdir
- Kurumsal ortam, altyapı ve firewall sorunları nedeniyle long polling, WebSocket kurulumuna göre daha basit ve daha uyumludur
Long polling uygulamasının ayrıntıları
getJobStatusSync fonksiyonu kritik rol oynar
jobId, owner, ttl gibi parametreleri alır ve belirli bir işin durumunu belli bir süre boyunca tekrar tekrar sorgular
- Aşağıdaki koşullardan biri sağlanana kadar tekrar sorgulama yapılır
- İş durumu
success veya failure olur
ttl (timeout) süresi dolar
- Veritabanı 500ms aralıklarla sorgulanır; sonuç kesinleşmemişse beklenir ve yeniden sorgulanır
- Timeout aşılırsa hata fırlatılır, başarılıysa sonuç döndürülür
Veritabanı optimizasyonu
- Sorgu maliyetini en aza indirmek için Postgres üzerinde uygun indeksler kullanılır
- Örnek:
CREATE INDEX idx_jobs_status ON jobs(id, cluster_id);
Long polling'in faydaları
- İzleme sürdürülebilirliği : mevcut HTTP tabanlı loglama ve izleme yığını aynen kullanılabilir
- Kimlik doğrulama basitliği : yeni bir kimlik doğrulama yöntemi uygulamaya gerek kalmadan mevcut HTTP kimlik doğrulaması kullanılabilir
- Altyapı uyumluluğu : firewall veya load balancer için ek ayar gerekmez; normal HTTP trafiği olarak ele alınır
- Operasyonel basitlik : sunucu yeniden başlatıldığında bağlantı durumunu ayrıca ele almak gerekmez ve debug daha kolaydır
- İstemci uygulamasının kolaylığı : standart HTTP istek-yanıt yapısına yalnızca yeniden deneme mantığı eklenmesi yeterlidir
ElectricSQL ile karşılaştırma
- ElectricSQL, Postgres verisini frontend ile senkronize eden bir çözümdür
- WebSocket yerine HTTP kullanırken de gerçek zamanlılığı garanti eden bir yapıya sahiptir
- Gerçek zamanlı güncellemeleri işlemek için aşırı kontrol veya düşük seviyeli bir yapı gerekmiyorsa pratikte ElectricSQL önerilir
Neden raw long polling seçtik
- Mesaj iletim mekanizması basit bir uygulama ayrıntısı değil, ürünün çekirdeği
- Bu temel özelliği üçüncü taraf bir kütüphaneye bağımlı kılamayız (kütüphane ne kadar iyi olursa olsun)
- Gereksinimler
- Çekirdek ürün kontrolü : mesaj iletim mekanizması üzerinde tam denetime sahip olmak gerekiyor. Bu bir altyapı meselesi değil, doğrudan ürünün kendisi
- Harici bağımlılıkları kaldırma : self-hosting'i basitleştirmek için dış bağımlılıkları en aza indirmek
- Düşük seviyeli kontrol : polling mekanizması ve bağlantı yönetimi üzerinde doğrudan kontrol
- Maksimum denetlenebilirlik : dinamik polling aralığı uygulamak gibi ayrıntıların ince ayarının yapılabilmesi
- Kod basitliği : kullanıcıların kod tabanını kolayca anlayıp değiştirebilmesi için sade bir tasarım
- Sonuç olarak basit bir HTTP Long Polling uygulaması seçilerek doğrudan kontrol ve basitlik sağlandı
Long polling uygularken dikkat edilmesi gerekenler
- TTL ayarı : sunucu tarafında mutlaka bir maksimum TTL zorunlu kılınmalı ve istemcinin istediği TTL bunun üstüne çıkmamalıdır
- Altyapı timeout'larını dikkate alma : TTL, load balancer, edge sunucu ve proxy gibi bileşenlerin timeout ayarlarından yeterince kısa olmalıdır
- DB polling aralığı : veritabanı yükünü azaltmak için yaklaşık 500ms gecikme verilmelidir
- Backoff stratejisi (isteğe bağlı) : polling aralığını kademeli artırarak sistem kaynakları daha verimli kullanılabilir
WebSocket düşünülmesi gereken durumlar
- WebSocket'in kendisi yanlış bir tercih değildir; başka açılardan faydalı olabilir
- Çok sayıda durum bilgili bağlantının izlenmesi ve karmaşık olayların sürekli çift yönlü aktarılması gerekiyorsa
- Kimlik doğrulama, altyapı ve gözlemlenebilirlik sorunlarını çözmek için yeterli kaynak ve zaman varsa
- Ancak operasyon, loglama, yeniden bağlanma yönetimi ve kimlik doğrulama mekanizmalarının doğrudan kurulmasını gerektiren bir karmaşıklık vardır
WebSockets: başka bir seçeneğe dair notlar
- Long Polling bizim ihtiyaçlarımıza uydu, ancak WebSockets de kesinlikle değerlendirmeye değerdir
- WebSockets kendi başına kötü değildir; sadece ciddi özen ve yönetim gerektirir
- WebSockets'in temel zorlukları ve çözüm yönleri
- Görünürlük : WebSockets durum temelli olduğundan, kalıcı bağlantılar için ek loglama ve izleme gerekir
- Kimlik doğrulama : WebSocket bağlantıları için yeni bir kimlik doğrulama mekanizması uygulanmalıdır
- Altyapı : WebSocket desteği için load balancer, firewall gibi altyapı bileşenleri uygun şekilde yapılandırılmalıdır
- Operasyon yönetimi : WebSocket bağlantılarının ve yeniden bağlantıların yönetimi; bağlantı timeout'ları ve hata işleme
- İstemci uygulaması : istemci tarafı WebSocket kütüphanesinin uygulanması; yeniden bağlanma ve durum yönetimi özellikleri dahil
5 yorum
ML model serving için burada bahsedilen
short pollingyapısını kullanıyoruz; hangi seçeneğin daha verimli olacağı konusunda epey düşünüyorum. Kendi çapımda biraz araştırdığım kadarıyla, WebSocket veya SSE gibi yöntemlerde yeniden bağlanma işlemlerinin maliyeti yüksek olduğu içinshort pollingin genel olarak daha güvenli olduğu söyleniyordu; ben de bu yüzdenshort pollingi seçtim.. 😭Long polling biraz
hackyhissettirdiği için insanlar bundan kaçınıyor gibi görünüyor. Tarayıcıda muhtemelen istek sürekli tamamlanmamış olarak görünecektir. Bazen yüklemesi hiç bitmeyen siteler oluyor; ben de "acaba içerik tamamen yüklenemedi mi?" diye düşündüğüm için pek hoşuma gitmiyor.Uygulama tarafında da sonuçta bir yerde
hangverip yanıt bekleyen bir duruma geçilecek; bu da biraz tuhaf görünüyor."Ajanın çalıştırma ve sohbet durumu güncellemelerini alması gerekiyor"
Bunu görünce aklıma hemen SSE geldi; gerçekten de Hacker News yorumlarında SSE'den çokça bahsedilmiş.
Hacker News yorumu
Long polling'in kendine özgü sorunları var
Phoenix ve LiveView'ı her gün kullanmak keyifli
Server-Sent Events (SSE) kullanmaya kıyasla teknik bir avantaj olup olmadığını merak ediyorum
Bu makale "Websocket" ile "Long-polling"i birbirinden bağımsız kararlar gibi bağlıyor
Node.js'te setTimeout kullanmanın daha kolay bir yolu
import { setTimeout } from "node:timers/promises"; await setTimeout(500);kullanınLong polling'i seviyorum; anlaması kolay ve istemci açısından çok yavaş bir bağlantı gibi çalışıyor
Server-Sent Events veya WebSockets, long polling'in tüm kullanım senaryolarının yerini alamaz
Postgres'in asenkron bildirim özelliğini kullanmak iyi olur
Kısa timeout'lar ve zarifçe sonlandırılan isteklerle long polling'in hâlâ anlamlı olup olmadığından emin değilim
WebSockets'e görece basit bir alternatifin hatırlatılması ferahlatıcı
Elixir, Phoenix framework ve LiveView üzerinden WebSockets kullanmayı denemek istiyorum.