14 Punkte von GN⁺ 2025-01-09 | 5 Kommentare | Auf WhatsApp teilen
  • 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

 
jhj0517 2025-01-10

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 … 😭

 
bbulbum 2025-01-10

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.

 
joyfui 2025-01-09

„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.

 
GN⁺ 2025-01-09
Hacker-News-Kommentare
  • Long Polling hat seine ganz eigenen Probleme

    • Second Life verwendet einen HTTPS-Long-Polling-Kanal zwischen Client und Server
    • Auf Client-Seite wird libcurl verwendet, und es kann zu Timeouts kommen
    • Wenn der Server zwischen einem Timeout und der nächsten Anfrage eine Nachricht senden will, kann eine Race Condition auftreten, durch die Nachrichten verloren gehen
    • Ein Apache-Server sitzt davor und blockiert unnötige Anfragen, aber Timeouts können trotzdem auftreten
    • Middleboxes und Proxy-Server mögen Long Polling möglicherweise nicht
    • Es gibt viele Komponenten, die es nicht mögen, HTTP-Verbindungen lange offen zu halten
    • Am Ende entsteht dadurch ein unzuverlässiger Nachrichtenkanal, sodass Sequenznummern nötig sind, um Duplikate zu erkennen, und Nachrichten verloren gehen können
    • Der im Originalartikel als "loop" markierte Diagrammabschnitt erwähnt die Behandlung von Timeouts nicht
    • Wenn man Long Polling verwendet, muss man alle paar Sekunden Daten senden, um die Verbindung aufrechtzuerhalten
  • Es ist eine Freude, Phoenix und LiveView täglich zu verwenden

    • Man nutzt WebSockets und muss sich darüber keine Gedanken machen
  • Ich frage mich, ob es technische Vorteile gegenüber der Nutzung von Server-Sent Events (SSE) gibt

    • Beide lassen eine HTTP-Verbindung offen und haben den Vorteil, einfaches HTTP zu sein
    • SSE scheint besser geeignet zu sein, wenn sich Updates oder Ergebnisse streamen lassen
    • Ein passender Anwendungsfall könnte sein, alle Job-IDs stellvertretend für einen bestimmten Client zu überwachen
  • Dieser Artikel verknüpft "Websocket" und "Long-polling" als voneinander unabhängige Entscheidungen

    • Ein Long-Polling-Server kann mit etwas zusätzlicher Arbeit auch WebSocket-Clients bedienen
    • Wenn die bestehende Architektur auf WebSockets basiert, braucht man zur Unterstützung von Long-Polling-Clients zwei Server-Schichten
  • Ein einfacherer Weg, setTimeout in Node.js zu verwenden

    • import { setTimeout } from "node:timers/promises"; await setTimeout(500); verwenden
  • Ich mag Long Polling, es ist leicht zu verstehen und funktioniert aus Sicht des Clients wie eine sehr langsame Verbindung

    • Man muss Retries und clientseitig abgebrochene Verbindungen nachverfolgen
    • Die Schleife im Codebeispiel, die wiederholt Daten abfragt, wirkt unbeholfen
  • Server-Sent Events oder WebSockets ersetzen nicht alle Anwendungsfälle von Long Polling

    • Das Verbindungslimit von SSE ist oft ein Problem
    • WebSockets sind in den meisten Umgebungen nicht zuverlässig
    • Das Problem, Änderungen im Backend zu erkennen und an die passenden Clients weiterzuleiten, ist damit immer noch nicht gelöst
  • Es ist sinnvoll, die asynchrone Benachrichtigungsfunktion von Postgres zu nutzen

    • Der Server kann auf einem Kanal LISTEN ausführen, und bei Datenänderungen kann PG TRIGGER und NOTIFY verwenden
  • Ich bin mir nicht sicher, ob Long Polling mit kurzen Timeouts und sauber beendeten Anfragen noch sinnvoll ist

    • Wenn HTTP/2 oder QUIC nicht verwendet werden, kann dieser Trick weiterhin sinnvoll sein
  • Es ist erfrischend, an eine relativ einfache Alternative zu WebSockets erinnert zu werden

    • Ich habe einmal in einem Startup gearbeitet, das sich für WebSockets entschieden hatte, und Tests in Hotel- und Restaurant-WLANs waren schwierig
 
luminance 2025-01-10

Ich würde WebSockets gern einmal mit Elixir, dem Phoenix-Framework und LiveView ausprobieren.