PgQue bringt Append-Only-Event-Queues in die Open-Source-Datenbank
Viele Produktionsinstanzen der Open-Source-Datenbank leiden unter Tabellen, die Millionen von Updates pro Tag erhalten, wodurch Autovacuum hunderte Male täglich ausgelöst wird und schwere Tabellenaufblähung entsteht. Dies führt zu schlechter Leistung, Parallelitätsproblemen und einer Datenbank, die schwer zu verwalten ist. In einem Blogbeitrag vom 28. September 2026 für Percona erklärt Autor Jobin Augustine, wie das Open-Source-Tool PgQue genau diese Probleme lösen kann, indem es update-intensive Statusverfolgung durch ein Append-Only-Event-Log ersetzt.
PgQue ist eine modernisierte Version von PgQ, einem Queueing-Framework, das ursprünglich von Marko Kreen als Teil von Skytools entwickelt wurde. Obwohl es über 20 Jahre lang weitgehend in Vergessenheit geriet, brachten Community-Mitglieder wie Alexander Kukushkin es durch Konferenzvorträge wie 'Open-source database queues done right with PgQ' auf der POSETTE 2026 wieder ins Blickfeld. Nikolay Samokhvalov überarbeitete später die Codebasis als PgQue, verbesserte die Dokumentation und erleichterte die Bereitstellung. Entscheidend ist, dass PgQue keine binären Erweiterungen benötigt.
Traditionelle Queue-Implementierungen in der Open-Source-Datenbank verwenden oft SQL-Funktionen wie FOR UPDATE SKIP LOCKED, aber das führt zu massiver Tabellenaufblähung und Parallelitätsengpässen. PgQue verfolgt einen anderen Ansatz: Es speichert Ereignisse in einer append-only partitionierten Tabelle und nutzt die Snapshots der Open-Source-Datenbank, um zu identifizieren, welche Transaktionen zwischen zwei Zeitpunkten abgeschlossen wurden. Consumer verfolgen ihre eigene Position mit einem Cursor, sodass keine Ereigniszeilen jemals an Ort und Stelle aktualisiert werden. Dadurch werden Aufblähung und Update-Kosten vollständig eliminiert, und mehrere Consumer können dasselbe Ereignisprotokoll unabhängig voneinander lesen.
Trotz seines Namens ist PgQue keine Queue im üblichen Sinne, da Ereignisse beim Konsumieren nie gelöscht werden. Stattdessen bewegt eine Bestätigung lediglich den Cursor des Consumers weiter. Das Modell ist ein gemeinsames, append-only Log, das einem Kafka-Topic ähnelt und besonders dem Subscription-Modell von Pulsar gleicht. Jeder Consumer behält seine eigene Position über dieselben Ereignisse, die nur einmal gespeichert werden. Allerdings fehlen PgQue die Partitionen von Kafka, sodass es keine Ordnung pro Schlüssel bietet, und sein Cursor zeigt auf einen Tick (einen Zeitscheibe) und nicht auf einen bestimmten Nachrichten-Offset. Ein Consumer committet über eine Tick-Nummer, nicht über eine Ereignis-ID. Die Speicherplatzfreigabe erfolgt durch Rotation und Abschneiden alter Tabellen, aber erst, nachdem der langsamste Consumer sie passiert hat. Das garantierte Rückspulfenster beträgt eine Rotationsperiode, die standardmäßig 2 Stunden beträgt; ein blockierter Consumer blockiert die Freigabe, anstatt Datenverlust zu verursachen. PgQue bietet außerdem Queue-Semantiken, die Kafka der Anwendung überlässt: pro-Consumer-Redelivery (ein fehlgeschlagenes Ereignis wird als private Kopie erneut angehängt, die nur für diesen Consumer sichtbar ist), eine eingebaute Dead-Letter-Queue und kooperative Consumer, die es mehreren Workern ermöglichen, einen einzigen logischen Cursor zu teilen. Das Feature der kooperativen Consumer ist laut Blogbeitrag als experimentell markiert.
Ereignisse werden in Batches gruppiert, was Netzwerk-Roundtrips reduziert, da eine einzelne Fetch-Anfrage mehrere Ereignisse zurückgeben kann. Nach der Verarbeitung eines Batches sendet der Consumer eine Bestätigung, und der Zeiger rückt zum nächsten Batch vor. Wichtig ist, dass die Bestätigung pro Batch und nicht pro Nachricht erfolgt. Wenn also eine Nachricht in einem Batch fehlschlägt, muss diese Nachricht negativ bestätigt werden, bevor der Batch bestätigt wird. Batch-Grenzen werden durch einen Ticker definiert, der in einem geplanten Intervall läuft, z. B. alle 100 Millisekunden (Standard) oder einem exakten Teiler von 1000 ms. Der Ticker wird einmal pro Sekunde von einer Cron-ähnlichen Scheduler-Erweiterung für die Datenbank ausgelöst und erzeugt innerhalb dieser Sekunde Ticks in der konfigurierten Rate. Nach jedem Tick sendet der Ticker ein LISTEN/NOTIFY-Ereignis, sodass Consumer auf Benachrichtigungen warten können, anstatt zu pollen. Bei Queues mit geringer Aktivität, bei denen seit dem letzten Tick weniger als 500 Ereignisse aufgelaufen sind, kann der Ticker gedrosselt werden; Benutzer können einen Tick manuell mit pgque.force_next_tick() erzwingen.
PgQue bietet unterschiedliche Sicherheitsrollen für Produzenten und Consumer: pgque_writer und pgque_reader sowie eine administrative Rolle pgque_admin. Zu den Kern-APIs gehören send(), send_batch(), subscribe(), unsubscribe(), receive(), ack() und nack(). Eine Reihe von Dead-Letter-Queue-Funktionen (dlq_inspect(), dlq_replay(), dlq_replay_all(), dlq_purge(), event_dead()) verschiebt wiederholt fehlgeschlagene Nachrichten in einen separaten Bereich. Idempotente Produzenten werden durch send_idem() und maint_idem() unterstützt, die Veröffentlichungen nach Schlüssel deduplizieren, sodass wiederholende Produzenten nicht doppelt veröffentlichen. Zu den Verwaltungsfunktionen gehören start(), stop(), status(), version(), set_tick_period_ms() und uninstall(). Observability-Funktionen wie queue_stats(), consumer_stats(), queue_health(), otel_metrics(), stuck_consumers(), throughput() und error_rate() liefern Statistiken, Health-Scoring, Erkennung blockierter Consumer und eine OpenTelemetry-kompatible Metrikansicht. Verzögerte Zustellung ist mit send_at() und zugehörigen Wartungsfunktionen möglich.
Um PgQue zu verwenden, kann eine Anwendung die rohen SQL-APIs aufrufen oder offizielle Client-Bibliotheken nutzen. Laut Blogbeitrag existieren Bibliotheken für Python, Go, TypeScript und Ruby im 'client'-Verzeichnis des Repositorys. Zum Testen über die Befehlszeilenschnittstelle der Datenbank kann ein Produzent ein Ereignis mit einer JSONB-Nutzlast in eine Queue veröffentlichen. Ein Consumer ruft pgque.receive() auf, um bis zu einer angegebenen Anzahl von Ereignissen abzurufen. Die receive-Funktion gibt Spalten wie msg_id, batch_id, type, payload, retry_count und created_at zurück. Leere Batches müssen nicht bestätigt werden; ihr Empfang schließt den Batch automatisch ab und bewegt den Cursor weiter. Nicht-leere Batches müssen mit ack() bestätigt werden, andernfalls geben nachfolgende Aufrufe denselben unbestätigten Batch zurück. Die Standard-Schleife lautet: Batch empfangen, Nachrichten verarbeiten (falls vorhanden), Batch bestätigen und wiederholen. Ein Beispielskript im Blog zeigt, wie Ereignisse abgelassen werden, bis der Consumer den neuesten Tick erreicht hat.
Jobin Augustine stellt fest, dass der Hype um 'Verwenden Sie einfach die Open-Source-Datenbank' wächst, da Systemdesigner die Komplexität durch externe Komponenten wie Kafka reduzieren möchten. Er verweist auf Chandan Shuklas kürzlichen Beitrag 'The Open-Source Database as a Workflow Engine: Building Reliable Long-Running AI Jobs Without Kafka' als Beispiel für diesen Trend. PgQue bietet ein fertiges Framework, das die Notwendigkeit vermeidet, snapshot-basierte Queueing-Logik neu zu erfinden. Für Teams, die die Open-Source-Datenbank in Produktion betreiben, kann die Übernahme von Mustern wie PgQue die Datenbankaufblähung reduzieren und die Skalierbarkeit verbessern. Eine verwaltete Cloud-Umgebung wie die verwalteten Datenbanken von AEU Data kann weiter helfen, indem sie automatisierte Backups, Überwachung und Hochverfügbarkeit bietet, sodass sich Teams auf die Anwendungslogik statt auf die Infrastrukturpflege konzentrieren können.