Tutorial Postgres LISTEN/NOTIFY untuk Real-Time Event-Driven Architecture
AW
Axel W

Dipublikasikan 25 Juli 2026

Tutorial Postgres LISTEN/NOTIFY untuk Real-Time Event-Driven Architecture

Postgres sering dianggap sebagai database relasional biasa, tapi fitur LISTEN/NOTIFY memungkinkannya berperan sebagai message broker ringan. Artikel ini adalah panduan praktis membangun event-driven architecture menggunakan mekanisme pub/sub bawaan Postgres tanpa perlu infrastruktur tambahan seperti Redis atau RabbitMQ.

LISTEN/NOTIFY memungkinkan aplikasi client menerima notifikasi real-time ketika data berubah di database. Dibanding polling berkala, pendekatan ini jauh lebih efisien dalam penggunaan resource dan latensi lebih rendah. Kita akan membahas lima langkah implementasi dari nol hingga production-ready.

Langkah 1: Setup Database dan Memahami Mekanisme Channel

Postgres menggunakan konsep channel untuk mengirim notifikasi. Sebelum aplikasi bisa menerima pesan, kamu perlu membuat mekanisme trigger yang memanggil NOTIFY.

-- Membuat tabel contoh
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    status VARCHAR(50) NOT NULL DEFAULT pending,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Membuat fungsi trigger
CREATE OR REPLACE FUNCTION notify_order_change()
RETURNS TRIGGER AS $$
BEGIN
    PERFORM pg_notify( order_updates , json_build_object( action , TG_OP, id , NEW.id, status , NEW.status)::text);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Memasang trigger ke tabel
CREATE TRIGGER trg_order_update
AFTER UPDATE ON orders
FOR EACH ROW
EXECUTE FUNCTION notify_order_change();

Fungsi di atas mengirim payload JSON ke channel order_updates setiap kali ada UPDATE pada tabel orders. Pastikan payload tidak terlalu besar karena NOTIFY membatasi pesan maksimal 8000 byte.

Langkah 2: Membuat Listener di Aplikasi Backend

Berikut implementasi listener menggunakan Node.js dengan library pg. Pola serupa bisa diterapkan di Python, Go, atau Rust menggunakan driver Postgres native.

const { Client } = require( pg );

const client = new Client({
    host: process.env.DB_HOST,
    database: process.env.DB_NAME,
    user: process.env.DB_USER,
    password: process.env.DB_PASS
});

async function startListener() {
    await client.connect();
    await client.query( LISTEN order_updates );

    client.on( notification , (msg) => {
        const payload = JSON.parse(msg.payload);
        console.log( Event received: , payload);
        // Logic bisnis: kirim ke WebSocket, queue job, dll
    });
}

startListener().catch(console.error);

Kunci di sini adalah menggunakan dedicated connection untuk LISTEN. Jangan gunakan connection pool untuk listener karena connection harus tetap aktif dan idle, bukan dipinjam-pinjamkan.

Langkah 3: Skalabilitas dengan Multiple Listeners

Salah satu pertanyaan umum: bagaimana kalau ada banyak instance aplikasi? Semua listener yang terhubung ke channel yang sama akan menerima notifikasi secara broadcast. Ini berarti setiap instance mendapat salinan pesan.

Untuk load balancing, gunakan pola berikut:

  • Broadcast: Cocok untuk cache invalidation. Semua instance menerima signal untuk flush cache lokal.

  • Queue pattern: Gunakan tabel sebagai queue jika hanya satu worker yang boleh memproses. LISTEN/NOTIFY bisa digunakan sebagai signal ada job baru, bukan delivery mekanisme utama.

  • Partition by channel: Buat channel dinamis berdasarkan resource ID. Misalnya order_updates_123 sehingga hanya service terkait yang listen.

Menurut laporan dari DBOS, Postgres LISTEN/NOTIFY sebenarnya sangat scalable dan bisa menangani ribuan listener concurrent tanpa degradasi signifikan.

Langkah 4: Menangani Reconnect dan Failure

Koneksi LISTEN bersifat persistent. Jika koneksi terputus, listener tidak otomatis recover. Kamu perlu implementasi reconnect logic:

  1. Gunakan health check heartbeat dengan SELECT 1 setiap interval tertentu.

  2. Tangani event end pada connection stream untuk mendeteksi disconnect.

  3. Gunakan exponential backoff sebelum reconnect agar tidak overwhelm server.

  4. Pertimbangkan menggunakan tools seperti PgBouncer atau pgpool dengan care. Transaction pooling bisa mengganggu LISTEN/NOTIFY karena session state hilang saat connection dipool-kan.

client.on( end , () => {
    console.warn( Connection lost, reconnecting... );
    setTimeout(startListener, 5000); // backoff 5 detik
});

Langkah 5: Monitoring dan Optimasi Production

Di production, pantau metrik berikut melalui pg_stat_activity dan log Postgres:

MetrikCara CekThreshold Warning
Idle listener countSELECT count(*) FROM pg_stat_activity WHERE state = idle> 500 connections
Notify queue depthLog file Postgres: notify queue is fullAny occurrence
Payload sizeValidasi di trigger atau aplikasi> 7 KB per message

Optimasi tambahan: jika payload kompleks, simpan detail di tabel dan kirimkan hanya ID via NOTIFY. Listener kemudian SELECT data lengkap berdasarkan ID yang diterima. Ini mengurangi beban notify queue secara drastis.

Postgres LISTEN/NOTIFY adalah solusi underrated untuk arsitektur event-driven ringan. Tanpa menambah stack infrastructure, kamu sudah punya pub/sub mechanism yang terintegrasi langsung dengan transactional boundary database.

Sumber referensi: DBOS Blog: Postgres LISTEN/NOTIFY Actually Scales, PostgreSQL Documentation: NOTIFY.