Echtzeitsysteme auf PostgreSQL entwickeln: Über das ORM hinaus

Team von Atlas Verified
·
13. Februar 2026
·
8 Min. Lesezeit
Echtzeitsysteme auf PostgreSQL entwickeln: Über das ORM hinaus

Benötigt eine Anwendung Echtzeitaktualisierungen, lautet die Standardantwort 2025: Firebase Realtime Database, Supabase Realtime, Pusher, Ably oder eine gehostete WebSocket-Schicht. Diese Dienste abstrahieren die Komplexität der Übermittlung an verbundene Clients. Sie funktionieren gut – bis sie es nicht mehr tun.

Bei Atlas Verified begannen wir mit einer betreuten Datenbankplattform einschließlich Echtzeitabonnements. Mit wachsendem System stießen wir an Grenzen der Abstraktion. Verbindungspooling, Echtzeitzuverlässigkeit, PostGIS-Raumabfragen und individuelle Triggerlogik führten uns in dieselbe Richtung: Kontrolle über die Datenbankschicht übernehmen.

Wir wechselten zu selbst gehostetem PostgreSQL und bauten das Echtzeitsystem mit dessen nativem LISTEN/NOTIFY und Server-Sent Events. Dieser Beitrag erklärt Entscheidung, Architektur und Erkenntnisse.

Warum wir die betreute Plattform verließen

Betreute Datenbankplattformen bieten überzeugenden Mehrwert: Authentifizierung, zeilenbasierte Sicherheit, Echtzeitabonnements, automatisch erzeugte APIs und Dashboard – sofort verfügbar. Für frühe Produkte ist das wertvoll. Eine funktionierende Anwendung entsteht ohne eine einzige Zeile Backend-Code.

Die Probleme entstehen, wenn die Anwendung reift und Anforderungen von Plattformannahmen abweichen.

Verbindungsgrenzen. Betreute Plattformen leiten Datenbankverbindungen oft durch einen Pooler mit eigenen Grenzen. Mehrere Backend-Dienste mit eigenen Pools – API-Server, Hintergrundaufträge, Echtzeitlistener – erschöpfen schnell die verfügbaren Verbindungen. Wir umgingen Poolergrenzen, statt Produktprobleme zu lösen.

Echtzeitzuverlässigkeit. Das Echtzeitsystem der betreuten Plattform nutzte Change Data Capture auf PostgreSQLs Write-Ahead Log. Architektonisch elegant, aber mit unkontrollierbaren Fehlerbildern. WAL-Replikation hat inhärente Latenz. Der Dienst fiel gelegentlich zurück und lieferte veraltete Updates. Wiederverbindung nach Netzunterbrechungen war unzuverlässig. Zur Fehlersuche benötigten wir Zugriff auf fremde Infrastruktur.

Erweiterungsanforderungen. Unsere Anwendung verarbeitet Handelsdokumente mit geografischen Daten: Hafenstandorte, Versandrouten, Schiffspositionen. PostGIS, die räumliche PostgreSQL-Erweiterung, ist dafür unerlässlich. Die Plattform unterstützte PostGIS, doch benötigte Versionen und Konfigurationen waren nicht immer verfügbar.

Individuelle Triggerlogik. Wir benötigten Datenbanktrigger für anwendungsspezifische Ereignisse statt nur allgemeiner „Zeile geändert“-Meldungen. Ändert sich der Verarbeitungsstatus eines Dokuments, muss der konkrete verbundene Client des Uploaders informiert werden. Die Plattform arbeitete auf Tabellenebene; wir benötigten Entitätsebene.

Die Migration war keine Kosten- oder Prinzipienfrage. Sie war pragmatisch: Wir verbrachten mehr Zeit mit dem Umgehen von Plattformgrenzen als mit einer eigenen Umsetzung nötig gewesen wäre.

LISTEN/NOTIFY: PostgreSQLs verborgenes Pub/Sub

PostgreSQL besitzt seit der 2010 veröffentlichten Version 9.0 einen integrierten Publish/Subscribe-Mechanismus. Obwohl über 15 Jahre alt, wird LISTEN/NOTIFY erstaunlich wenig genutzt. Die meisten Entwickler kennen ihn nicht.

Der Mechanismus ist einfach. Jede Datenbankverbindung kann mit LISTEN einen benannten Kanal abonnieren. Jede Verbindung kann mit NOTIFY alle Listener dieses Kanals informieren. Die Nutzlast ist eine Zeichenfolge mit maximal 8,000 Bytes.

Was LISTEN/NOTIFY für Echtzeitanwendungen leistungsfähig macht:

Kein zusätzlicher Infrastrukturaufwand. Kein separater Nachrichtenbroker muss bereitgestellt, überwacht oder skaliert werden. Mit PostgreSQL haben Sie Pub/Sub. Kein Redis, RabbitMQ oder Kafka für diesen konkreten Anwendungsfall.

Transaktionale Garantien. NOTIFY-Nachrichten werden erst beim Commit der umgebenden Transaktion zugestellt. Fügt eine Transaktion eine Zeile ein und sendet eine Meldung, wird diese bei Rollback nie zugestellt. Externe Broker bieten das ohne sorgfältige Orchestrierung nicht.

Triggerintegration. PostgreSQL-Trigger können pg_notify aufrufen. So kann die Datenbank Listener bei bestimmten Änderungen automatisch informieren, ohne Anwendungscode im Schreibpfad.

Die Grenzen sind real und sollten verstanden werden:

Keine Persistenz. Lauscht beim NOTIFY niemand, geht die Nachricht verloren. Keine Wiederholung, Warteschlange oder Zustellgarantie für getrennte Clients. Listener müssen Wiederverbindung und Zustandswiederherstellung übernehmen.

Nutzlastgröße. Die Grenze von 8,000 Bytes verhindert große Objekte in NOTIFY. Wir senden minimale Nutzlasten – Tabellenname, Vorgangstyp und Primärschlüssel – und lassen den Client bei Bedarf vollständige Daten abrufen.

Auf eine Datenbank begrenzt. LISTEN/NOTIFY funktioniert innerhalb einer PostgreSQL-Instanz, nicht über Replikate oder Cluster. Wenn alle Schreibvorgänge zu einer primären Instanz gehen, ist das keine Einschränkung.

Unsere Architektur: Trigger, SSE und Kanalmultiplexing

So passen die Teile zusammen.

Datenbanktrigger werden auf Tabellen mit relevanten Echtzeitaktualisierungen installiert: Dokumente, Nachrichten, Gespräche, Benachrichtigungen, Sendungsverifizierungen und mehr. Bei Einfügen, Aktualisieren oder Löschen einer Zeile ruft der Trigger pg_notify mit JSON-Nutzlast aus Tabellenname, Vorgangstyp, Primärschlüssel und Filterspalten auf.

Eine dedizierte Listener-Verbindung unseres API-Servers führt LISTEN auf dem Benachrichtigungskanal aus und empfängt alle Triggerereignisse. Diese langlebige PostgreSQL-Verbindung ist vom Abfragepool getrennt und lauscht ausschließlich auf Meldungen.

Ein Ereignisbus empfängt Listener-Meldungen und leitet sie anhand von Abonnements an Clients weiter. Ein Client mit Dokumentenabonnement für eine konkrete Sendungs-ID erhält nur Meldungen zu deren Dokumenten.

Server-Sent Events liefern Meldungen an den Browser. Jeder Client öffnet eine SSE-Verbindung zum API-Server und nennt gewünschte Kanäle und Filter. Der Ereignisbus gleicht eingehende Meldungen mit Abonnements ab und sendet passende Ereignisse über die jeweiligen SSE-Verbindungen.

Warum SSE statt WebSockets

Mit dieser Entscheidung sind wir dauerhaft zufrieden. Server-Sent Events sind in jeder für unseren Anwendungsfall relevanten Dimension einfacher als WebSockets.

Bewusst unidirektional. Unsere Echtzeitanforderungen gehen vom Server zum Client: Der Server meldet Datenänderungen. Der Client muss keine Echtzeitnachrichten zurücksenden; er nutzt Standard-HTTP-Anfragen. SSE passt zu unserer tatsächlichen Anforderung.

Nativ HTTP. SSE-Verbindungen sind normale HTTP-Anfragen mit bestimmtem Inhaltstyp. Sie funktionieren ohne Sonderkonfiguration durch HTTP-Proxys, Load Balancer und CDNs. WebSockets benötigen HTTP Upgrade, das manche Proxys falsch behandeln und manche Unternehmensfirewalls vollständig blockieren.

Automatische Wiederverbindung. Die EventSource-API des Browsers übernimmt Wiederverbindung nativ. Bei Verbindungsabbruch verbindet er sich mit exponentiell wachsendem Abstand erneut. Bei WebSockets müssen Sie diese Logik selbst implementieren – und mindestens einmal falsch machen.

Den Vorteil von WebSockets – bidirektionale Kommunikation – benötigen wir einfach nicht. Für Echtzeitnachrichten vom Client zum Server sind WebSockets richtig. Für Benachrichtigungen vom Server zum Client ist SSE richtig.

Eine Abfrageschicht ohne ORM entwickeln

Mit der betreuten Plattform verließen wir auch ihre automatisch erzeugte Abfrage-API. Unser API-Server benötigte eine Datenbankzugriffsschicht.

Standardoptionen sind ORMs wie Prisma, TypeORM oder Drizzle oder direktes SQL mit Treiber. Wir wählten einen dritten Weg: einen schlanken Query Builder mit verkettbarer API, der darunter direktes SQL erzeugt und ausführt.

Vertraute API. Unser vorhandener Code nutzte die Clientbibliothek der Plattform. Eine kompatible API bedeutete minimale Migrationsänderungen. Bisherige Clientabfragen konnten mit identischer Syntax durch unseren Query Builder laufen.

Keine Magie. ORMs erzeugen SQL, das Sie nicht sehen und manchmal nicht steuern können. Bei langsamen Abfragen müssen Sie generiertes SQL lesen, das oft wenig Ähnlichkeit zum ORM-Aufruf hat. Unser Query Builder erzeugt vorhersehbares SQL: Jede verkettete Methode entspricht einer konkreten Klausel; die Abfrage ist immer einsehbar.

Eingebettete Joins. Handelsdaten sind relational. Eine Sendung hat Dokumente, diese Knoten und diese Prüfungen. Verschachtelte Auswahl mit verschachtelten Ergebnissen ohne jedes Mal manuell komplexes JOIN-SQL verbessert die Entwicklererfahrung erheblich.

Das Ergebnis ist eine wenige Hundert Codezeilen große Abfrageschicht, die die meisten Datenbankinteraktionen abdeckt und immer lesbares, optimierbares SQL erzeugt.

Budgetierung von Verbindungspools

Bei selbst gehostetem PostgreSQL verantworten Sie das Verbindungsbudget. max_connections definiert die absolute Obergrenze, und jede Verbindung verbraucht Speicher.

Verbindungen als Budget – endliche, gemeinsam genutzte Ressource – zu betrachten, verhindert Probleme, die unvorbereitete Teams treffen.

Jeder Dienst deklariert seine maximale Verbindungszahl. Ihre Summe muss deutlich unter dem PostgreSQL-Limit bleiben, mit Reserve für spontane Verbindungen – psql-Sitzungen, Migrationen, Überwachung. Das klingt offensichtlich, doch die meisten Teams beginnen erst beim Produktionsausfall durch erschöpfte Verbindungen. Frühzeitige Budgetierung ersparte uns diesen Alarm um 3 Uhr morgens.

Sichere Migrationen im großen Maßstab

Der Wechsel zu selbst gehostetem PostgreSQL bedeutete den Umzug Hunderter Tabellen, Funktionen, Trigger und Indizes. Die größere Herausforderung war jedoch ein Migrationssystem, das mit der Anwendungsentwicklung weiter funktioniert.

Unser automatisiertes System nutzt mit Zeitstempeln benannte Dateien in einem eigenen Verzeichnis. Ein Runner durchsucht es, vergleicht mit einer Tabelle bereits ausgeführter Migrationen und führt ausstehende der Reihe nach aus. Jede läuft in einer Transaktion – bei Fehler wird sauber zurückgesetzt und die Bereitstellung bis zur Behebung blockiert.

Der Runner läuft automatisch bei jeder Bereitstellung vor Anwendungsstart. Schemaänderungen erfolgen damit ohne manuelle Schritte im Bereitstellungsprozess. Ein Entwickler erstellt eine SQL-Datei mit Zeitstempel, überträgt sie ins Repository und die nächste Bereitstellung wendet sie an.

Sicherheitsentscheidungen: Migrationen nutzen idempotente Schutzbedingungen, um nach teilweiser Anwendung nicht zu scheitern. Jede muss ein eigenständiges SQL-Skript ohne menschliche Eingabe sein. Bei Fehler wird die Transaktion vollständig zurückgesetzt – die Anwendung startet nicht mit teilweise geändertem Schema.

Was wir gewonnen haben

Selbst gehostetes PostgreSQL mit eigenem Echtzeitsystem erforderte erhebliche Entwicklungsinvestition. Der Nutzen war jedoch beträchtlich:

Zuverlässigkeit. Echtzeitmeldungen treffen Millisekunden nach Datenänderungen ein. Keine WAL-Replikationsverzögerung, keine Drittanbieterausfälle im kritischen Pfad.

Fehlersuchbarkeit. Bei Problemen können wir den gesamten Pfad verfolgen: Trigger, Meldung, Ereignisbus, SSE-Zustellung. Jeder Schritt liegt in unserem Code, unseren Protokollen und unserer Infrastruktur.

Flexibilität. Echtzeit für eine neue Tabelle erfordert eine einzige SQL-Migration zur Triggerinstallation. Keine Plattformkonfiguration, kein Fremdanbieter-Dashboard, kein Supportticket.

Leistung. Direkte PostgreSQL-Verbindungen mit abgestimmtem Pool übertreffen Verbindungen durch betreute Plattforminfrastruktur. Die Abfragelatenz sank nach der Migration deutlich.

Der Kompromiss ist betriebliche Verantwortung. Wir verwalten Datenbank, Backups, Replikationsüberwachung und Kapazität. Für ein mit PostgreSQL-Betrieb vertrautes Team ist das machbar. Andernfalls können Plattformabstraktionen trotz ihrer Grenzen sinnvoll sein.


Atlas Verified läuft auf selbst gehostetem PostgreSQL mit eigener Echtzeitinfrastruktur und verarbeitet täglich Tausende Handels-Compliance-Verifizierungen in der globalen Lieferkette.

Hat Ihnen dieser Artikel gefallen?

Teilen Sie ihn mit Ihrem Netzwerk