- In einem auf Node.js/TypeScript basierenden Backend mussten umfangreiche Echtzeit-Updates verarbeitet werden
- Mit PostgreSQL als Backend mussten Hunderte von Worker-Nodes kontinuierlich nach neuen Jobs suchen, und Agents mussten Updates zu Ausführungs- und Chat-Status erhalten
- Die Untersuchung begann mit WebSockets, endete jedoch bei einer überraschend effektiven „altmodischen“ Lösung
→ „HTTP Long Polling mit Postgres“
Das Problem: Echtzeit-Updates in großem Maßstab
- Updates für Worker-Nodes :
- Es gibt Hunderte von Worker-Nodes, auf denen Node.js/Golang/C# SDKs laufen
- Sie mussten sofort erfahren, wenn neue Jobs verfügbar waren. Deshalb war eine Abfragestrategie nötig, die die Postgres-Datenbank nicht in die Knie zwingt
- Synchronisierung des Agent-Status :
- Agents benötigten Echtzeit-Updates zu Ausführungs- und Chat-Status, und diese mussten effizient gestreamt werden
Vergleich von Long Polling und WebSocket
- Short Polling ist wie ein Zug, der strikt nach Fahrplan abfährt und in festen Intervallen losfährt, unabhängig davon, ob Fahrgäste da sind
- Beim Long Polling wartet der Server mit der Antwort; sobald Daten vorhanden sind, gibt er sie sofort zurück, und nach einer bestimmten Zeit antwortet er mit einem Timeout
- Es ist also wie ein Zug, der „wartet und losfährt, sobald Fahrgäste da sind“. Nur wenn innerhalb einer bestimmten Zeit (TTL) keine Fahrgäste auftauchen, fährt er leer ab
- Es vereint zwei Vorteile: Bei Daten (Fahrgästen) geht es sofort los, und wenn keine da sind, werden Ressourcen effizient genutzt
- WebSocket hält die Verbindung dauerhaft offen und ermöglicht bidirektionalen Datenaustausch
- In Unternehmensumgebungen sowie wegen Infrastruktur- und Firewall-Themen ist Long Polling oft einfacher und kompatibler als eine WebSocket-Konfiguration
Details zur Long-Polling-Implementierung
- Die Funktion
getJobStatusSync übernimmt eine zentrale Rolle
- Sie nimmt Parameter wie
jobId, owner und ttl entgegen und fragt den Status eines bestimmten Jobs für einen festgelegten Zeitraum wiederholt ab
- Die wiederholte Abfrage läuft, bis eine der folgenden Bedingungen erfüllt ist
- Der Job-Status wird
success oder failure
ttl (Timeout) ist abgelaufen
- Die Datenbank wird in Intervallen von 500 ms abgefragt; wenn das Ergebnis noch nicht feststeht, wird gewartet und erneut abgefragt
- Wird das Timeout überschritten, wird ein Fehler ausgelöst; bei Erfolg wird das Ergebnis zurückgegeben
Datenbankoptimierung
- In Postgres werden passende Indizes gesetzt, um die Abfragekosten zu minimieren
- Beispiel:
CREATE INDEX idx_jobs_status ON jobs(id, cluster_id);
Vorteile von Long Polling
- Einfache Pflege von Monitoring : Bestehende HTTP-basierte Logging- und Monitoring-Stacks können unverändert weiterverwendet werden
- Einfache Authentifizierung : Es ist nicht nötig, ein neues Authentifizierungsverfahren zu implementieren; die bestehende HTTP-Authentifizierung kann unverändert verwendet werden
- Infrastruktur-Kompatibilität : Für Firewalls oder Load Balancer sind keine separaten Einstellungen nötig, da der Verkehr als normaler HTTP-Traffic behandelt wird
- Operative Einfachheit : Auch bei Server-Neustarts muss kein separater Verbindungszustand behandelt werden, und Debugging ist einfacher
- Einfache Client-Implementierung : Es reicht, zur Standardstruktur aus HTTP-Request und -Response eine Retry-Logik hinzuzufügen
Vergleich mit ElectricSQL
- ElectricSQL ist eine Lösung zur Synchronisierung von Postgres-Daten mit dem Frontend
- Sie verwendet HTTP statt WebSockets und stellt dennoch Echtzeitfähigkeit sicher
- Wenn für die Verarbeitung von Echtzeit-Updates in der Praxis keine extreme Kontrolle oder Low-Level-Strukturen nötig sind, wird ElectricSQL empfohlen
Warum wir uns für Raw Long Polling entschieden haben
- Der Mechanismus zur Nachrichtenübermittlung ist nicht nur ein einfaches Implementierungsdetail, sondern ein zentraler Bestandteil des Produkts
- Auf Bibliotheken von Drittanbietern kann man sich für Kernfunktionen nicht verlassen (egal wie gut sie sind)
- Anforderungen
- Kontrolle über das Kernprodukt : Der Mechanismus zur Nachrichtenübermittlung muss vollständig kontrollierbar sein. Es geht nicht um Infrastruktur, sondern um das Produkt selbst
- Entfernung externer Abhängigkeiten : Externe Abhängigkeiten sollen minimiert werden, um Self-Hosting zu vereinfachen
- Low-Level-Kontrolle : Polling-Mechanismus und Verbindungsmanagement sollen direkt kontrolliert werden
- Maximale Kontrollierbarkeit : Details wie dynamische Polling-Intervalle müssen feinjustierbar sein
- Einfacher Code : Das Design soll bewusst simpel sein, damit Nutzer die Codebasis leicht verstehen und anpassen können
- Letztlich wurde durch die Wahl einer einfachen HTTP-Long-Polling-Implementierung direkte Kontrolle und Einfachheit erreicht
Worauf man bei der Implementierung von Long Polling achten sollte
- TTL festlegen : Serverseitig muss unbedingt eine maximale TTL erzwungen werden, und die vom Client angeforderte TTL darf diese nicht überschreiten
- Infrastruktur-Timeouts berücksichtigen : Die TTL sollte deutlich kürzer sein als die Timeout-Einstellungen von Load Balancern, Edge-Servern oder Proxys
- DB-Polling-Intervall : Eine Verzögerung von etwa 500 ms reduziert die Last auf der Datenbank
- Backoff-Strategie (optional) : Durch schrittweise längere Polling-Intervalle lassen sich Systemressourcen noch effizienter nutzen
Wann man WebSocket in Betracht ziehen sollte
- WebSocket an sich ist nicht falsch und kann in anderen Aspekten sehr nützlich sein
- Wenn viele zustandsbehaftete Verbindungen überwacht und fortlaufend komplexe Events in beide Richtungen ausgetauscht werden müssen
- Wenn genügend Zeit und Ressourcen vorhanden sind, um Authentifizierung, Infrastruktur und Beobachtbarkeit zu lösen
- Gleichzeitig gibt es die zusätzliche Komplexität, Betrieb und Logging, Reconnect-Verhalten und Authentifizierungsmechanismen selbst aufzubauen
WebSockets: Eine weitere Option
- Long Polling passte gut zu unseren Anforderungen, aber auch WebSockets sind definitiv eine ernsthafte Überlegung wert
- WebSockets an sich sind nicht schlecht; sie benötigen lediglich viel Sorgfalt und Management
- Zentrale Herausforderungen bei WebSockets und mögliche Lösungsrichtungen
- Sichtbarkeit : Da WebSockets zustandsbasiert sind, müssen zusätzlich Logging und Monitoring für dauerhafte Verbindungen eingeführt werden
- Authentifizierung : Für WebSocket-Verbindungen muss ein neuer Authentifizierungsmechanismus implementiert werden
- Infrastruktur : Load Balancer, Firewalls und andere Infrastruktur müssen passend konfiguriert werden, um WebSockets zu unterstützen
- Betriebsmanagement : Verwaltung von WebSocket-Verbindungen und Wiederverbindungen sowie Behandlung von Connection-Timeouts und Fehlern
- Client-Implementierung : Implementierung einer clientseitigen WebSocket-Bibliothek einschließlich Reconnect- und State-Management-Funktionen
5 Kommentare
Wir verwenden für das Serving von ML-Modellen die hier erwähnte „Short-Polling“-Struktur und überlegen viel, was effizienter wäre. Nach allem, was ich hier und da recherchiert habe, heißt es, dass Short Polling im Allgemeinen sicherer ist, weil die Wiederverbindung bei WebSockets oder SSE mit hohen Kosten verbunden ist. Deshalb haben wir uns zwar für Short Polling entschieden, aber … 😭
Long Polling wirkt etwas hacky, deshalb schreckt es wohl viele ab. Im Browser würde es vermutlich ständig so aussehen, als wäre eine Anfrage noch nicht abgeschlossen. Es gibt ja gelegentlich Websites, bei denen das Laden nie fertig zu werden scheint, und ich frage mich dann immer: Wurden die Inhalte nicht vollständig geladen? Das gefällt mir nicht besonders.
Auch in der Anwendung würde man letztlich irgendwo einen Hang einbauen und auf die Antwort warten müssen … das wirkt etwas unnatürlich.
„Der Agent muss Ausführungs- und Chat-Status-Updates erhalten“
Dabei musste ich sofort an SSE denken, und wie erwartet wird in den Hacker-News-Kommentaren auch oft SSE erwähnt.
Hacker-News-Kommentare
Long Polling hat seine ganz eigenen Probleme
Es ist eine Freude, Phoenix und LiveView täglich zu verwenden
Ich frage mich, ob es technische Vorteile gegenüber der Nutzung von Server-Sent Events (SSE) gibt
Dieser Artikel verknüpft "Websocket" und "Long-polling" als voneinander unabhängige Entscheidungen
Ein einfacherer Weg,
setTimeoutin Node.js zu verwendenimport { setTimeout } from "node:timers/promises"; await setTimeout(500);verwendenIch mag Long Polling, es ist leicht zu verstehen und funktioniert aus Sicht des Clients wie eine sehr langsame Verbindung
Server-Sent Events oder WebSockets ersetzen nicht alle Anwendungsfälle von Long Polling
Es ist sinnvoll, die asynchrone Benachrichtigungsfunktion von Postgres zu nutzen
LISTENausführen, und bei Datenänderungen kann PGTRIGGERundNOTIFYverwendenIch bin mir nicht sicher, ob Long Polling mit kurzen Timeouts und sauber beendeten Anfragen noch sinnvoll ist
Es ist erfrischend, an eine relativ einfache Alternative zu WebSockets erinnert zu werden
Ich würde WebSockets gern einmal mit Elixir, dem Phoenix-Framework und LiveView ausprobieren.