4 poin oleh GN⁺ 2023-09-24 | 1 komentar | Bagikan ke WhatsApp
  • Untuk meneruskan perubahan Postgres ke sistem lain secara real-time, diperlukan CDC (Change Data Capture), dan tiap opsi—mulai dari notifikasi sederhana hingga replikasi berbasis WAL—memiliki perbedaan besar dalam keandalan dan beban operasional
  • Listen/Notify adalah cara paling ringan untuk memulai, tetapi karena semantik pengiriman at-most-once, notifikasi yang bersifat sementara, dan batas payload 8000 byte, pendekatan ini lebih cocok sebagai sinyal pendukung daripada CDC inti
  • Polling tabel dan tabel audit (outbox pattern) bisa diimplementasikan hanya dengan tabel dan trigger standar, tetapi deteksi penghapusan, diff, urutan commit, write amplification, dan backpressure harus ditangani sendiri
  • Replikasi logis (logical replication) adalah cara yang kuat untuk melakukan streaming insert/update/delete dari WAL, tetapi aplikasi harus mengelola replication slot, ack, restart, hingga penanganan throughput
  • Sequin, yang dibangun di atas replikasi logis Postgres, meneruskan perubahan ke SQS, Kafka, Elasticsearch, Redis, HTTP endpoint, dan lainnya sehingga mengurangi beban mengelola replication slot secara langsung

Kapan Postgres CDC dibutuhkan

  • Postgres kuat untuk mengelola data yang tersimpan, tetapi jika Anda ingin memicu workflow dari perubahan tabel atau melakukan streaming secara real-time ke penyimpanan data, sistem, atau layanan lain, maka perpindahan data harus dirancang secara terpisah
  • Change Data Capture (CDC) adalah pendekatan untuk mengidentifikasi dan menangkap perubahan database, lalu meneruskannya secara real-time ke sistem downstream
  • Ada beberapa cara untuk menangkap perubahan di Postgres, dan masing-masing berbeda dalam tingkat kesulitan implementasi, keandalan, dan beban operasional

Listen/Notify: pub-sub paling sederhana

  • Listen/Notify di Postgres adalah fitur komunikasi antarproses yang bekerja dengan pola publish-subscribe
  • Sebuah sesi dapat listen pada channel tertentu, dan aktivitas database atau sesi lain dapat mengirim notify ke channel tersebut
  • Untuk change capture, fitur ini bisa digunakan dengan menambahkan trigger
    • Contoh trigger membuat JSON berisi table, id, dan action dari record yang berubah pada saat after insert or update or delete, lalu memanggil pg_notify('table_changes', payload::text)
  • Keterbatasannya cukup jelas
    • Memiliki semantik pengiriman at-most-once, dan listener harus terhubung saat notifikasi diterbitkan
    • Listener hanya menerima notifikasi sejak mulai berlangganan, jadi jika koneksi sempat terputus karena masalah jaringan, notifikasi bisa terlewat
    • Batas ukuran payload adalah 8000 byte, dan jika melebihi batas ini, perintah notify akan gagal
    • Ukuran payload juga mencakup nama channel, dan seperti identifier Postgres, nama channel dapat memiliki panjang maksimum 64 byte
  • Cocok untuk deteksi perubahan dasar atau optimasi polling tabel, tetapi mungkin kurang sesuai untuk kebutuhan CDC yang lebih kompleks

Polling tabel: sederhana tetapi lemah untuk penghapusan dan diff

  • Cara change capture yang paling sederhana namun andal adalah dengan melakukan polling langsung pada tabel
  • Tiap tabel memerlukan kolom seperti updated_at yang diperbarui setiap kali baris diubah, dan bila perlu bisa dibuat dengan trigger
  • Kombinasi updated_at dan id digunakan sebagai cursor, lalu logika aplikasi menyimpan dan mengelola cursor tersebut
  • Jika digabungkan dengan langganan Notify, aplikasi dapat diberi tahu bahwa ada insert atau update record sehingga frekuensi polling bisa dikurangi
    • Karena notifikasi Postgres bersifat sementara, pendekatan ini lebih tepat dipakai hanya sebagai optimasi di atas polling
  • Ada tiga kelemahan utama
    • Baris yang dihapus tidak lagi ada di tabel, sehingga deteksi penghapusan tidak dimungkinkan
    • Sebagai solusi, trigger delete bisa menyimpan id dan kolom yang diperlukan ke tabel terpisah seperti deleted_contacts, lalu aplikasi melakukan polling ke tabel itu
    • Anda bisa tahu bahwa sebuah record telah di-update, tetapi tidak bisa tahu apa yang berubah
    • Karena datetime dan sequence di Postgres bisa tidak selaras dengan urutan commit, saat membaca blok berdasarkan updated_at, Anda bisa melewatkan baris yang masih dalam proses commit
  • Ini adalah pilihan yang masuk akal untuk pelacakan perubahan sederhana ketika penghapusan, diff, dan kehilangan sesekali bukan masalah besar

Tabel audit: menyimpan log perubahan dengan outbox pattern

  • Pendekatan tabel audit (audit table) mencatat perubahan ke tabel changelog terpisah, dan juga dikenal sebagai outbox pattern
  • changelog dapat memiliki kolom-kolom terkait perubahan
    • action: apakah insert, update, atau delete
    • old: jsonb dari record sebelum perubahan, kosong untuk insert
    • values: jsonb dari field yang berubah, kosong untuk delete
    • inserted_at: waktu terjadinya perubahan
  • Untuk mengimplementasikannya, Anda memerlukan fungsi trigger yang melakukan insert ke changelog setiap kali ada perubahan, serta trigger untuk setiap tabel yang ingin dipantau
  • changelog juga bisa dikonsumsi seperti antrean
    • Worker aplikasi mengambil perubahan dari tabel
    • Untuk pemrosesan yang mendekati exactly-once, Anda bisa menggunakan for update skip locked dari Postgres
    • Worker dapat membuka transaksi, mengunci satu batch dengan order by timestamp limit 100 for update skip locked, memprosesnya, menghapus record yang sudah selesai diproses, lalu commit
  • Ada kelemahan dari sisi operasional
    • Satu penulisan ke tabel utama dapat menghasilkan beberapa penulisan ke tabel audit, yaitu write amplification
    • Umumnya akan ada setidaknya tiga kali penulisan: insert awal ke tabel audit, update saat diproses, dan delete setelah selesai diproses
    • Pendekatan fan-out lewat worker harus dirancang sendiri sesuai kebutuhan aplikasi
    • Sebelum deployment skala produksi, kemungkinan Anda perlu menyesuaikan fungsi trigger dan desain tabel
    • Anda juga mungkin perlu mempertimbangkan kebijakan detail seperti batas waktu berapa lama worker boleh mempertahankan perubahan yang sudah di-checkout
    • Jika worker gagal memproses dengan sukses, tabel audit akan terus menumpuk sehingga penanganan backpressure kurang memadai

Foreign Data Wrapper: opsi yang lebih dekat ke sinkronisasi antar-Postgres tertentu

  • Foreign Data Wrapper (FDW) adalah fitur yang memungkinkan database Postgres membaca dan menulis ke sumber data eksternal
  • Ekstensi berbasis FDW yang paling luas didukung adalah postgres_fdw
    • Anda dapat menghubungkan dua database Postgres dan membuat struktur mirip view di satu database yang merujuk ke tabel di database lain
    • Secara internal, satu database Postgres bertindak sebagai klien dan database lainnya sebagai server
    • Saat foreign table di-query, database klien mengirim query ke database server melalui wire protocol Postgres
  • FDW bukan pendekatan yang umum untuk change capture dan sulit direkomendasikan di luar situasi yang sangat spesifik
  • Jika Anda ingin menulis perubahan dari satu database Postgres ke database Postgres lain, FDW bisa cocok
    • Contohnya adalah ketika database akuntansi dan database aplikasi dipisahkan
    • Anda bisa melewati tahap change capture perantara dan langsung merefleksikan perubahan antar-database dengan postgres_fdw
  • Anda juga bisa membuat FDW sendiri untuk melakukan POST perubahan ke API internal
    • Karena penulisan ke API terjadi di dalam commit, API bisa menolak perubahan dan menyebabkan commit di-rollback
  • FDW memang kuat, tetapi jarang menjadi pilihan terbaik untuk CDC, dan menulis FDW sendiri termasuk pekerjaan paling besar di antara pendekatan change capture
    • Menulis FDW sendiri menjadi lebih mudah dengan alat seperti Supabase wrappers, tetapi tetap merupakan pekerjaan besar

Replikasi logis langsung: CDC kuat berbasis WAL

  • Postgres memiliki protokol untuk replikasi database, dan salah satunya adalah replikasi logis (logical replication)
  • Replikasi logis dibangun di atas WAL (write-ahead log) Postgres
    • Semua insert, update, dan delete di database dilacak
    • Perubahan di-streaming ke subscriber
  • Pengguna terlebih dahulu membuat replication slot di primary
    • Bentuknya menggunakan pg_create_logical_replication_slot('<your_slot_name>', '<output_plugin>')
  • output_plugin menentukan plugin untuk mendekode perubahan WAL
    • pgoutput adalah plugin bawaan, dan mengeluarkan format biner yang diharapkan oleh server klien
    • test_decoding adalah plugin output sederhana yang menyediakan perubahan WAL dalam bentuk yang dapat dibaca manusia
    • Meski bukan plugin bawaan Postgres, wal2json adalah plugin populer, dan JSON lebih mudah ditangani sebagai titik awal aplikasi dibanding format biner Postgres
  • Setelah replication slot dibuat, proses dapat dimulai dan dikonsumsi
    • Replication slot menggunakan area protokol Postgres yang berbeda dari query standar
    • Banyak library klien menyediakan fungsi untuk membantu pekerjaan dengan replication slot
    • Contoh psycopg2 menggunakan cursor.start_replication(...) dan cursor.consume_stream(...) untuk mengonsumsi pesan WAL, lalu mengirim ack dengan cursor.send_feedback(flush_lsn=msg.wal_end)
  • Klien harus melakukan ack terhadap pesan WAL yang diterima, dan replication slot bekerja mirip Kafka dengan offset
  • Replikasi logis adalah pendekatan andal yang memang dibuat untuk CDC, tetapi kompleks
    • Replication slot dan replication protocol lebih tidak familier bagi developer dibanding tabel dan query biasa
    • Anda memerlukan strategi agar tidak kehilangan pesan saat restart
    • Sistem juga harus dirancang agar mampu menangani volume besar pesan yang keluar dari Postgres

Sequin: alat CDC yang membungkus replikasi logis

  • Sequin adalah alat CDC yang meneruskan perubahan dan baris dari Postgres ke antrean, stream, indeks pencarian, cache, HTTP endpoint, dan lain-lain
  • Targetnya mencakup SQS, Kafka, Elasticsearch, Redis, HTTP endpoints, dan lainnya
  • Sequin secara internal menggunakan replikasi logis Postgres, tetapi mengabstraksikan kompleksitas protokol level rendah
  • Semua insert, update, dan delete bisa ditangkap, dan untuk update serta delete, Sequin menangkap nilai new dan old dari baris
  • Kondisi yang cocok untuk mempertimbangkan Sequin adalah sebagai berikut
    • Anda membutuhkan CDC real-time
    • Anda ingin melakukan streaming langsung ke tujuan seperti SQS atau webhook tanpa sistem perantara
    • Anda membutuhkan fitur seperti backfill data historis dan pemfilteran perubahan berbasis klausa SQL where
    • Anda membutuhkan alternatif yang lebih sederhana daripada mengelola replication slot secara langsung
    • Anda membutuhkan jaminan pemrosesan exactly-once
  • Ada juga kekurangannya
    • Sequin bukan ekstensi internal Postgres, melainkan alat pihak ketiga yang berjalan di samping database
    • Karena bukan ekstensi, alat ini memiliki kompatibilitas luas dengan berbagai database Postgres, tetapi jika tidak menggunakan Sequin Cloud, Anda harus menyiapkan infrastruktur tambahan sendiri

Kriteria pemilihan

  • Pada tahap awal, Listen/Notify dan polling tabel cocok digunakan
    • Listen/Notify bagus untuk menangkap event yang tidak kritis, prototyping, dan optimasi polling
    • Polling adalah solusi yang wajar dan lurus untuk use case sederhana
  • Pada tahap yang sedikit lebih serius, tabel audit bisa menjadi pilihan menengah
    • Bisa menangkap payload new dan old dari baris
    • Jika dibuat dengan benar, Anda bisa mendapatkan sistem pemrosesan exactly-once
    • Saat skala membesar, write amplification dan tidak adanya backpressure menjadi masalah, dan kesalahan dalam konfigurasi manual bisa membuat pesan terlewat
  • Pada tahap skala, replikasi logis adalah solusi yang paling mendekati pendekatan andal
    • Namun, lebih disarankan memakai alat seperti Sequin daripada membaca langsung dari slot
  • FDW adalah fitur yang menarik, tetapi kecil kemungkinannya menyelesaikan kebutuhan CDC yang umum

1 komentar

 
GN⁺ 2023-09-24
Komentar Hacker News
  • Trigger + tabel histori (tabel audit) adalah jawaban yang tepat untuk 98% kasus. Kalau belum memakainya, mulai saja hari ini. Ini teknik yang sudah teruji selama lebih dari 30 tahun
    Contoh sederhana untuk mengimplementasikannya secara generik ada di https://gist.github.com/slotrans/353952c4f383596e6fe8777db5d.... Pendekatan ini mengorbankan efisiensi ruang demi “implementasi yang mudah”
    Kalau bisa menyimpan data immutable, itu sangat bagus, tetapi di database kemungkinan ada sangat banyak data mutable, dan setiap hari besar kemungkinan banyak hal terlupakan. Jangan lupa, pakai tabel histori saja
    Referensi: https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
    Sebaiknya jangan memakai library atau teknik pelacakan histori di lapisan aplikasi seperti Papertrail. Itu lambat, rentan error, dan tidak bisa menangkap perubahan DB yang melewati stack aplikasi. Upaya memberi timestamp updated dari aplikasi juga pada dasarnya keliru, karena setiap web server punya jam yang berbeda. Harus memakai jam DB, dan itulah satu-satunya jam yang benar

    • Demi konsistensi, jangan membuat waktu di client; masukkan pemanggilan seperti now() di dalam query untuk memakai jam DB
      Namun melakukan sinkronisasi hanya dengan timestamp ini tidak cukup. Sebab timestamp dibuat pada saat transaksi dimulai, bukan saat transaksi di-commit
      Jika mem-poll tabel dan memfilter berdasarkan timestamp terbaru, sebagian transaksi dengan urutan commit yang saling terselip bisa terlewat. Bisa saja dibuat zona penyangga dengan mengambil data beberapa menit lebih jauh ke masa lalu lalu menghapus duplikat, tetapi di PostgreSQL durasi transaksi tidak dibatasi, dan mengambil terlalu jauh ke masa lalu sangat boros. Jika akurasi dan efisiensi penting, cara ini tidak tepat
    • Estuary (https://estuary.dev, saya CTO-nya) membuat change log data lake real-time berisi seluruh perubahan database di cloud storage, tanpa konfigurasi tambahan pada DB produksi
      Dengan log sequence number, waktu DB, dan REPLICA IDENTITY FULL, status sebelum/sesudah perubahan juga ikut tercakup. Setelah itu, jika collection dimaterialisasi ke tempat seperti Snowflake, secara default Anda mendapatkan tabel sinkronisasi yang mengikuti update dari DB sumber
      Dari data lake dasar yang sama, histori tabel lengkap untuk tujuan audit juga bisa ditransformasi atau dimaterialisasi, sehingga tidak perlu lagi memasang capture atau WAL reader pada DB sumber
    • Dengan mereferensikan session variable di trigger, informasi tambahan seperti komentar tentang alasan perubahan bisa dimasukkan ke histori. Saya baru mencobanya di proyek pribadi kecil, tetapi sejauh ini berjalan baik
    • Saya mem-porting contoh tersebut ke SQLite dan mendemonstrasikan cara kerjanya: https://chat.openai.com/share/b5113cb1-10df-4a38-adde-5ec0e7...
      Saya juga menjelaskan pendekatan SQLite yang mengimplementasikan pola serupa berbasis kolom, bukan JSON: https://simonwillison.net/2023/Apr/15/sqlite-history/
    • Pendekatan ini bagus, dan sebenarnya saya juga membuat activity feed aplikasi dengan cara ini. Namun ini sendiri tidak menyelesaikan masalah “mendorong perubahan keluar”. Tentu saja, jika mendengarkan perubahan WAL pada tabel audit, Anda bisa mendapatkan kedua manfaatnya sekaligus
  • Artikel ini merangkum dengan ringkas berbagai pendekatan yang bisa dilakukan dengan fitur bawaan Postgres
    Pada bagian “menangkap perubahan ke tabel audit”, di perusahaan sebelumnya kami memakai pola Temporal Tables dengan baik. Berbeda dari RDBMS besar lainnya, Postgres sendiri tidak memilikinya sebagai fitur bawaan, tetapi ada pola sederhana yang bisa dimanfaatkan sebagai fungsi SQL: https://github.com/nearform/temporal_tables
    Anda bisa melihat keadaan tabel pada titik waktu tertentu, sehingga dapat menjawab pertanyaan seperti “apa pengaturan pengguna ini pada 12 Agustus?”, “berapa banyak record yang belum diproses pada pukul 23:55 tadi malam?”, atau “tunjukkan perbedaan feature flag saat ini dengan seminggu lalu”

  • Saya pernah berkonsultasi di sebuah perusahaan yang dulu memiliki SQL Server monolitik yang sangat besar. Memang bukan Postgres, tetapi anggap saja Postgres pun situasinya akan mirip
    Selama puluhan tahun beroperasi, database itu dipakai untuk segala macam keperluan di dalam perusahaan, dan pada praktiknya semua aplikasi serta proses bisnis di seluruh perusahaan menyimpan data ke database ini
    Masalahnya, ada banyak aplikasi yang membaca DB ini, dan ada sangat banyak proses serta prosedur yang menyisipkan dan mengubah data, sehingga ketika proses insert/update di hulu berubah atau ditambahkan, hal itu sampai melanggar invariant di level aplikasi. Proses yang normal pun berperilaku berbeda jika ada data yang buruk
    Sangat sulit melacak penyebabnya, karena hal-hal yang diperiksa biasanya ditulis 10 tahun lalu dan para pegawainya sudah meninggalkan perusahaan
    Saya penasaran apakah perubahan pada database Postgres bisa ditangkap dalam semacam bentuk DAG, sehingga kita bisa tahu proses mana yang menyisipkan, mengubah, atau menghapus data, bagaimana perilakunya secara historis, bagaimana berbagai aplikasi membaca data ini, dan bagaimana statistik kueri berubah seiring waktu
    Saya tidak begitu tahu apakah ada preseden untuk ini, atau pendekatan seperti apa yang bisa dipakai untuk membuat alat semacam itu. Dulu saya pernah terpikir membuat sesuatu yang mirip, tetapi rasanya ini wilayah yang membutuhkan pemahaman setingkat engineer inti Postgres agar bisa membuat pilihan yang baik

    • Replikasi logis Postgres berisi semua pernyataan perubahan untuk merekonstruksi status yang sama secara logis di database lain, yaitu informasi insert, update, dan delete
      Untuk setiap perubahan, Anda tidak mendapatkan data asal di level klien
      Meski begitu, ada jalan memutar. Stream replikasi logis juga bisa memuat pesan informasi dari fungsi pg_logical_emit_message, jadi klien dapat memasukkan metadata sendiri. Mungkin juga bisa dikonfigurasi agar identifier klien dipancarkan saat setiap transaksi dimulai
    • Saya tidak tahu bagaimana menangani kueri, tetapi untuk insert/update saya memakai kolom yang melacak sumber event (last updated by). Bisa jadi ini antipattern, jadi akan bagus jika ada solusi yang lebih kokoh
    • Secara teknis, replikasi log berisi semua operasi yang dilakukan semua pelaku, dan jika trigger dipakai dengan hati-hati, semuanya juga bisa dilacak lewat tabel capture DDL/DML. Jika DCL dikhawatirkan, itu juga bisa disertakan
      Pendekatan seperti ini bekerja pada hampir semua solusi keluarga SQL yang memakai WAL atau trigger
      Di SQL Server, saya sudah beberapa kali memakai pendekatan trigger, tetapi jika semua kueri di-log, biasanya menjadi lambat. Merancang mekanisme insert yang tidak menghambat operasi produksi tidaklah sempurna, dan sampling mungkin diperlukan
    • Dengan membuat tiap aplikasi memiliki user DB sendiri saja, Anda sudah bisa mendapatkan cukup banyak informasi
    • Ada ide untuk menelusuri semua skrip dan program yang mengirim kueri ke DB, lalu menambahkan komentar ID unik pada tiap kueri yang mengarah kembali ke skrip/program tersebut. Jika komentar dan ID itu tertinggal di log kueri, sepertinya asalnya bisa dilacak
  • Jika ingin mengambil jalur “tabel audit”, cukup pakai pgaudit. Ini ekstensi yang sudah teruji di praktik nyata, dan jika memakai AWS, juga bisa digunakan di RDS
    https://github.com/pgaudit/pgaudit/blob/master/README.md
    https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appen...

  • Tidak perlu dilakukan. Menginginkan hal ini berarti mengubah relasi Postgres menjadi kontrak. Tidak ada layanan yang bisa mempersistenkan state internalnya
    Jika benar-benar berkomitmen pada domain-driven design, mungkin saja bisa, tetapi lebih baik memakai sistem berbasis event yang ringan namun praktis

    • Relasi database, suka atau tidak, sudah merupakan kontrak
      Sesuatu yang berbasis event 1000 kali lebih kompleks
  • Cara polling kolom updated_at tidak kokoh dalam bentuk yang paling sederhana. Sebab tidak ada jaminan transaksi akan commit dalam urutan itu

    • Saya penulisnya. Catatan bagus. Misalnya transaksi A dimulai, before trigger berjalan, dan updated_at pada Row 1 disetel ke 2023-09-22 12:00:01
      Tak lama kemudian transaksi B dimulai, updated_at pada Row 2 disetel ke 2023-09-22 12:00:02, dan B commit lebih dulu
      Kueri polling berjalan, melihat Row 2 sebagai perubahan terbaru lalu memperbarui cursor ke 2023-09-22 12:00:02; jika A kemudian commit belakangan, Row 1 akan terlewat
      Cara sederhana untuk menghindari masalah ini adalah tidak melakukan polling nyaris real-time. Urutannya pada akhirnya akan menjadi konsisten
      Saran yang lebih kokoh mungkin adalah memakai sequence. Misalnya membuat kolom updated_at_idx yang bertambah setiap kali baris berubah
    • Ini baru saya tahu. Apakah itu tetap terjadi jika memakai trigger untuk memperbarui kolom?
      Saya penasaran apakah dengan before trigger yang mengisi now(), timestamp updated_at pada dua baris tetap bisa berbeda dari urutan commit transaksi. updated_at dan commit timestamp tidak harus sama, tetapi updated_at harus merepresentasikan urutan commit secara akurat hingga level milidetik/mikrodetik
    • Untuk polling, alih-alih updated_at, gunakan kolom _txid yang disetel trigger ke ID transaksi saat ini. Lalu saat polling berikutnya, gunakan txid_current() untuk memeriksa transaksi mana yang sudah commit dan mana yang belum
      Agak riskan dan sangat mudah menimbulkan error di nilai batas, tetapi sudah berjalan baik di produksi selama beberapa tahun
  • Tulisannya bagus
    Jika memakai Elixir dan Postgres, saya membuat library kecil dengan pendekatan serupa untuk mendengarkan perubahan WAL: https://github.com/cpursley/walex

  • Semua pendekatan ini agak kurang memuaskan, dan secara pribadi saya melihat polling sebagai yang paling praktis
    Saya berharap Postgres berinovasi di area ini

    • Pernah ada berbagai upaya untuk memasukkan beberapa jenis temporalitas sebagai fitur kelas satu ke dalam standar SQL
      Sebelum masuk ke standar SQL, menurut saya sulit muncul momentum di ruang kernel DBMS relasional. Opsinya banyak dan kompleks, sementara solusi yang berhasil di ruang pengguna pun umumnya tidak memberikan beban berlebihan dari sisi performa
      Sebagai catatan, orang-orang yang meneliti bidang ini umumnya condong ke pendekatan tabel audit. Alasannya, pendekatan itu menjaga sifat ACID yang konsisten di dalam database, dan mempertahankan Postgres sebagai single point of failure alih-alih menambahkan proxy atau pekerjaan polling
    • Apakah interval polling 1 detik praktis?
  • Ada celah besar di dunia data. Alih-alih menanyakan hasil ke penyimpanan data, akan lebih baik jika hasil kueri didorong secara inkremental
    Saya banyak melakukan analitik real-time dan streaming; pemrosesan stream bisa dilakukan, dan sebagian juga bisa ditangani sebagai materialized view di dalam penyimpanan data. Namun setelah data masuk ke DB atau data lake, untuk melihat perubahan di downstream pada dasarnya kita kembali lagi ke polling
    Jika ingin bereaksi ketika kondisi tertentu terjadi di data, atau memperbarui layar tanpa refresh halaman, tidak banyak solusi yang rapi. Solusi dalam tulisan ini pun terlihat lebih seperti workaround daripada fitur kelas satu
    Jika ingin membuat laporan yang diperbarui real-time tanpa refresh halaman, biasanya caranya adalah memuat data dari DB lalu mengalirkan perubahan ke GUI lewat Kafka dan WebSocket. Akibatnya kita mengoperasikan arsitektur lambda yang aneh, dengan sebagian analitik ditangani dalam kode dan sebagian di DB
    Ada inovasi di area ini. KSQL dan Kafka Streams bisa mengeluarkan perubahan, Materialize punya subscription, dan ClickHouse punya live view. Namun banyak fiturnya masih baru atau tahap pratinjau dan tidak benar-benar pas. Saya sudah mencoba semuanya, tetapi rasanya terlalu banyak pekerjaan dilemparkan ke developer
    Akan bagus jika ada library yang bisa langsung memberi feed perubahan lewat opsi seperti [select * from orders with suscribe]. Ini area yang cukup penting, tetapi selama ini kurang mendapat perhatian

  • Ada jebakan besar pada replikasi yang tidak dibahas dalam tulisan itu, dan karena itulah saya tidak memakai replikasi
    Postgres berusaha memberi jaminan yang sangat kuat agar konsumen slot replikasi tidak kehilangan data. Jadi jika konsumen tidak mengonsumsi data dari slot, Postgres dengan baik hati terus menyimpan data yang terlewat, sampai akhirnya disk penuh dan DB tumbang. Saya mengalaminya di dua DB SaaS berbeda saat prototyping, dan satu-satunya cara memulihkannya adalah membuat tiket dukungan
    Jika konsumen slot replikasi berhenti membaca, alarm wajib berbunyi
    Alasan lain adalah jalur kode untuk mengambil snapshot awal tabel dan jalur kode untuk membaca perubahan benar-benar berbeda. Menginisialisasi pembacaan slot replikasi agar tidak ada perubahan yang terlewat bukan hal sepele
    Sayangnya, dari sudut pandang change capture, replikasi adalah solusi yang paling tidak terasa hacky
    Saya memakai polling, tetapi menyimpan txid alih-alih updated_at

    • Alih-alih membiarkan slot terus menahan ruang, Anda bisa menetapkan batas ukuran agar slot ditandai tidak valid saat melewati ukuran tertentu: https://www.postgresql.org/docs/current/runtime-config-repli...
      Saya penasaran perilaku seperti apa yang lebih Anda inginkan
      Jika menangani volume data besar, snapshot awal dan pembacaan perubahan memang cenderung ingin diproses secara berbeda. Sebab kita perlu bisa melakukan hal-hal seperti inisialisasi paralel atau inisialisasi berbasis backup fisik. Meski begitu, saya paham bahwa fitur untuk men-stream data lama secara selektif setelah slot dibuat bisa berguna
      Bagian menginisialisasi pembacaan slot replikasi agar perubahan tidak terlewat sepertinya tidak semestinya sulit; saya penasaran di bagian mana Anda tersangkut
    • Salah satu trik untuk menangani masalah pertama adalah mengirim pesan logical decoding ke diri sendiri. Dengan begitu, WAL yang disimpan bisa dijaga tetap rendah
      Saat tidak membutuhkan semua perubahan, slot replikasi sementara yang membersihkan dirinya sendiri ketika koneksi terputus juga berguna. Ada juga konfigurasi untuk menetapkan batas maksimum WAL yang disimpan agar server tidak mati
    • Saya pernah terjebak masalah ini. Benar-benar subtil. Rasanya seolah-olah menghapus konsumen tidak seharusnya berdampak apa pun pada DB utama, tetapi kenyataannya justru menciptakan bom waktu
      Akan menarik kalau Anda bisa menjelaskan lebih lanjut bagaimana memakai txid alih-alih updated_at