← Tutti gli articoli

PgQue porta code di eventi append-only al database open-source

PgQue porta code di eventi append-only al database open-source

Molte istanze di produzione del database open-source soffrono di tabelle che ricevono milioni di aggiornamenti al giorno, attivando l'autovacuum centinaia di volte al giorno e causando un grave table bloat. Questo porta a prestazioni scadenti, problemi di concorrenza e un database difficile da gestire. In un post del 28 settembre 2026 sul blog di Percona, l'autore Jobin Augustine spiega come lo strumento open-source PgQue può risolvere esattamente questi problemi sostituendo il tracciamento dello stato basato su aggiornamenti pesanti con un log di eventi append-only.

PgQue è una versione modernizzata di PgQ, un framework di code originariamente creato da Marko Kreen come parte di Skytools. Sebbene sia stato in gran parte dimenticato per oltre 20 anni, membri della comunità come Alexander Kukushkin lo hanno riportato all'attenzione attraverso interventi a conferenze come 'Open-source database queues done right with PgQ' al POSETTE 2026. Nikolay Samokhvalov ha successivamente aggiornato il codebase come PgQue, migliorando la documentazione e rendendolo più facile da distribuire. Fondamentalmente, PgQue non richiede l'installazione di estensioni binarie.

Le implementazioni tradizionali di code nel database open-source spesso usano funzionalità SQL come FOR UPDATE SKIP LOCKED, ma questo porta a un enorme table bloat e colli di bottiglia di concorrenza. PgQue adotta un approccio diverso: memorizza gli eventi in una tabella partizionata append-only e usa gli snapshot del database open-source per identificare quali transazioni sono state completate tra due punti nel tempo. I consumer tracciano la propria posizione con un cursore, quindi nessuna riga di evento viene mai aggiornata sul posto. Questo elimina completamente il bloat e i costi di aggiornamento e consente a più consumer di leggere lo stesso log di eventi in modo indipendente.

Nonostante il nome, PgQue non è una coda nel senso usuale perché gli eventi non vengono mai eliminati quando vengono consumati. Invece, un acknowledgment semplicemente fa avanzare il cursore di quel consumer. Il modello è un log condiviso append-only, più vicino a un topic Kafka e particolarmente simile al modello di sottoscrizione di Pulsar. Ogni consumer mantiene la propria posizione sugli stessi eventi, che vengono memorizzati una sola volta. Tuttavia, PgQue non ha le partizioni di Kafka, quindi non fornisce un ordinamento per chiave, e il suo cursore punta a un tick (una fetta di tempo) piuttosto che a un offset di messaggio specifico. Un consumer committa attraverso un numero di tick, non attraverso un ID di evento. La bonifica dello spazio funziona ruotando e troncando le vecchie tabelle, ma solo dopo che il consumer più lento le ha superate. La finestra di riavvolgimento garantita è un periodo di rotazione, che è di 2 ore per impostazione predefinita; un consumer bloccato blocca la bonifica invece di causare perdita di dati. PgQue offre anche semantica di coda che Kafka lascia all'applicazione: redelivery per consumer (un evento fallito viene riaccodato come copia privata visibile solo a quel consumer), una dead-letter queue integrata e consumer cooperativi che consentono a più worker di condividere un singolo cursore logico. La funzionalità di consumer cooperativi è contrassegnata come sperimentale al momento del post.

Gli eventi sono raggruppati in batch, il che riduce i round trip di rete perché una singola richiesta di fetch può restituire più eventi. Dopo aver elaborato un batch, il consumer invia un acknowledgment e il puntatore avanza al batch successivo. È importante notare che l'acknowledgment è per batch, non per messaggio, quindi se un messaggio in un batch fallisce, quel messaggio deve essere negativamente riconosciuto prima che il batch venga riconosciuto. I confini del batch sono definiti da un ticker che viene eseguito a un intervallo pianificato, come ogni 100 millisecondi (l'impostazione predefinita) o qualsiasi divisore esatto di 1000 ms. Il ticker viene attivato una volta al secondo da un'estensione di pianificazione simile a cron per il database, e entro quel secondo produce tick alla velocità configurata. Dopo ogni tick, il ticker emette un evento LISTEN/NOTIFY, consentendo ai consumer di attendere le notifiche invece di fare polling. Sulle code a bassa attività, dove si sono accumulati meno di 500 eventi dall'ultimo tick, il ticker può essere limitato; gli utenti possono forzare manualmente un tick con pgque.force_next_tick().

PgQue fornisce ruoli di sicurezza distinti per produttori e consumatori: pgque_writer e pgque_reader, più un ruolo amministrativo pgque_admin. Le API principali includono send(), send_batch(), subscribe(), unsubscribe(), receive(), ack() e nack(). Un insieme di funzioni per la dead-letter queue (dlq_inspect(), dlq_replay(), dlq_replay_all(), dlq_purge(), event_dead()) sposta i messaggi ripetutamente falliti in un'area separata. I produttori idempotenti sono supportati tramite send_idem() e maint_idem(), che deduplicano le pubblicazioni per chiave in modo che i produttori che ritentano non pubblichino due volte. Le funzioni di gestione includono start(), stop(), status(), version(), set_tick_period_ms() e uninstall(). Le funzioni di osservabilità come queue_stats(), consumer_stats(), queue_health(), otel_metrics(), stuck_consumers(), throughput() e error_rate() forniscono statistiche, punteggio di salute, rilevamento di consumer bloccati e una vista delle metriche compatibile con OpenTelemetry. La consegna ritardata è possibile con send_at() e relative funzioni di manutenzione.

Per usare PgQue, un'applicazione può chiamare le sue API SQL grezze o usare librerie client ufficiali. Al momento del post, esistono librerie per Python, Go, TypeScript e Ruby nella directory 'client' del repository. Per i test dall'interfaccia a riga di comando del database, un produttore può pubblicare un evento in una coda con un payload JSONB. Un consumer chiama pgque.receive() per recuperare fino a un numero specificato di eventi. La funzione receive restituisce colonne tra cui msg_id, batch_id, type, payload, retry_count e created_at. I batch vuoti non richiedono acknowledgment; riceverli completa automaticamente il batch e fa avanzare il cursore. I batch non vuoti devono essere riconosciuti con ack(), altrimenti le chiamate successive restituiscono lo stesso batch non riconosciuto. Il ciclo standard è: ricevere un batch, elaborare i messaggi se presenti, riconoscere il batch e ripetere. Uno script di esempio nel blog mostra come drenare gli eventi finché il consumer non raggiunge l'ultimo tick.

Jobin Augustine nota che il clamore attorno a 'Usa semplicemente il database open-source' sta crescendo poiché i progettisti di sistemi vogliono ridurre la complessità derivante da componenti esterni come Kafka. Cita il recente post di Chandan Shukla 'The Open-Source Database as a Workflow Engine: Building Reliable Long-Running AI Jobs Without Kafka' come esempio di questa tendenza. PgQue fornisce un framework pronto all'uso che evita la necessità di reinventare la logica di accodamento basata su snapshot. Per i team che eseguono il database open-source in produzione, adottare pattern come PgQue può ridurre il bloat del database e migliorare la scalabilità. Un ambiente cloud gestito come i database gestiti di AEU Data può aiutare ulteriormente fornendo backup automatizzati, monitoraggio e alta disponibilità, consentendo ai team di concentrarsi sulla logica applicativa piuttosto che sulla manutenzione dell'infrastruttura.

Disponibile anche in: EnglishDeutschShqip