1 poin oleh GN⁺ 3 jam lalu | Belum ada komentar. | Bagikan ke WhatsApp
  • Untuk menjawab pertanyaan seperti seperti apa data pada tanggal tertentu (Selasa lalu), Postgres 19 memperkenalkan dukungan tabel temporal native (temporal table), sehingga data sebelum dan sesudah perubahan dapat dilacak tanpa sistem trigger audit terpisah
  • Standar SQL:2011 mendefinisikan tabel temporal lebih dari 10 tahun lalu, dan Postgres akhirnya memasukkannya ke core, bergabung lebih lambat dibanding mesin DB lain
  • Alih-alih dua kolom valid_from/valid_to dan exclusion constraint berbasis ekstensi btree_gist, Postgres menyediakan ekspresi yang lebih intuitif lewat satu kolom range type dan constraint WITHOUT OVERLAPS
  • Dengan sintaks FOR PORTION OF, pemecahan baris otomatis saat UPDATE·DELETE ditangani oleh engine, sekaligus mencegah celah dan tumpang tindih pada sumbu waktu
  • Pengenalan ini mencakup separuh dari sistem bi-temporal, yaitu application time, belum mendukung system time, tetapi menyiapkan fondasi untuk rilis mendatang

Cara Lama - The Old-Fashioned Way

  • Upaya pertama untuk melacak harga produk secara kronologis terdiri dari dua kolom tanggal valid_from, valid_to dan CHECK constraint valid_from < valid_to
    • Namun ini tidak mencegah penyisipan dua baris dengan rentang tanggal yang tumpang tindih untuk produk yang sama (misalnya produk nomor 42 berharga $9.99 sekaligus $14.99 pada hari Selasa yang sama)
  • Solusi tradisional adalah menggunakan ekstensi btree_gist dan exclusion constraint
    • Dalam bentuk EXCLUDE USING gist (product_id WITH =, daterange(valid_from, valid_to) WITH &&), akan muncul error saat memasukkan baris yang tumpang tindih
  • Masalah dari pendekatan ini
    • GiST adalah indeks khas Postgres yang membutuhkan pengalaman, dan karena berupa ekstensi opsional, ada hambatan masuk
    • Sintaks exclusion constraint tidak intuitif, sehingga sulit terpikir sebagai pendekatan standar
    • Tabel itu sendiri tidak memiliki kesadaran temporal bawaan; saat rentang waktu berubah, baris harus dipecah atau digabung secara manual, dan beban konsistensi temporal dialihkan ke aplikasi

Sejarah Singkat Waktu - A Brief History of Time

  • Standar SQL:2011 memperkenalkan periode waktu valid (APPLICATION TIME), constraint WITHOUT OVERLAPS, dan sintaks FOR PORTION OF untuk manipulasi data temporal
  • Henrietta Dombrovskaya (Hetti) bersama Chad Slaughter mengembangkan ekstensi pg_bitemporal, sebuah framework untuk mengelola tabel bi-temporal di dalam Postgres dengan PL/pgSQL
    • Sejak 2015, mereka mempresentasikan konsep ini di berbagai konferensi, mendemonstrasikan cara melacak valid time (waktu ketika fakta benar di dunia nyata) dan transaction time (waktu ketika DB mencatat fakta) secara bersamaan
  • Perbedaan dua dimensi waktu
    • Valid time berarti "harga ini berlaku dari Januari hingga Juni"
    • Transaction time adalah sudut pandang DB: "baris ini disisipkan pada 12 Maret pukul 15:47, lalu diganti pada 3 April pukul 09:01"
    • Dengan menggabungkan keduanya, tabel bi-temporal dapat menjawab "berdasarkan informasi yang diketahui saat itu, berapa harga yang kita kira berlaku pada Selasa lalu"
  • pg_bitemporal menggunakan EXCLUDE USING gist dua kali, masing-masing untuk rentang effective (valid time) dan rentang asserted (transaction time)
    • Menyediakan fungsi untuk penyisipan, pembaruan, koreksi, penonaktifan, dan penghapusan bi-temporal, serta implementasi relasi interval Allen (Allen's interval relationships) untuk penalaran temporal
  • Batasan ekstensi
    • Tidak dapat mengubah query planner agar mengenali kondisi temporal, berintegrasi dengan sistem constraint level engine, atau menyediakan sintaks manipulasi native → perlu masuk ke core
    • Postgres 19 menerima separuh valid time dari sistem bi-temporal; belum lengkap, tetapi merupakan kemajuan besar

Rentang sebagai Penyelamat - Ranges to the Rescue

  • Pendekatan Postgres 19 menggunakan satu kolom range type valid_at DATERANGE, bukan valid_from/valid_to terpisah
    • Dengan PRIMARY KEY (product_id, valid_at WITHOUT OVERLAPS), ekstensi btree_gist dan exclusion constraint tidak lagi diperlukan
    • WITHOUT OVERLAPS memastikan product_id unik pada setiap titik waktu, tetapi tetap mengizinkan beberapa baris untuk produk yang sama selama rentangnya tidak tumpang tindih
  • Secara internal masih menggunakan indeks GiST dan memerlukan btree_gist untuk kolom non-temporal pada key, tetapi Postgres otomatis menangani dependensi saat inisialisasi constraint
  • Notasi rentang: [ berarti inklusif, ) berarti eksklusif → [2025-01-01, 2025-07-01) mencakup 1 Januari dan tidak mencakup 1 Juli
    • Baris Gadget terakhir [2026-01-01,) adalah rentang open-ended tanpa tanggal akhir yang ditentukan untuk harga saat ini
  • Perilaku perlindungan terhadap tumpang tindih
    • Saat menyisipkan rentang salah [2025-03-01, 2025-01-01), muncul error bahwa "batas bawah rentang harus kurang dari atau sama dengan batas atas"
    • Saat menyisipkan rentang tumpang tindih [2025-03-01, 2025-09-01), muncul error pelanggaran exclusion constraint products_pkey
    • Dengan menggunakan range saja, dua jenis validasi didapat sekaligus

Mengiris dan Membagi - Slicing and Dicing

  • Jika harga sebuah produk diubah menjadi $10.99 hanya untuk Maret–September 2025, cara lama mengharuskan baris dipecah dan disisipkan secara manual, sehingga kesalahan bisa menimbulkan celah atau tumpang tindih
  • Pada tabel temporal, maksud tersebut dapat diekspresikan secara langsung
    • UPDATE products FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-09-01' SET price = 10.99 WHERE product_id = 1
  • Hasilnya, baris Widget bertambah dari 3 menjadi 5
    • Baris $9.99 yang lama dipersempit menjadi [2025-01-01, 2025-03-01)
    • Baris baru $10.99 ditambahkan untuk rentang yang tersisa
    • Baris $12.99 yang lama dipersempit menjadi [2025-09-01, 2026-01-01)
    • Baris baru $10.99 ditambahkan untuk rentang tersisa [2025-07-01, 2025-09-01)
  • Alasan $10.99 terbagi menjadi dua baris adalah karena FOR PORTION OF memproses setiap baris yang cocok secara independen, dan tidak melakukan penggabungan (coalesce) rentang yang bersebelahan setelahnya
    • Hasil akhirnya tidak memiliki celah atau tumpang tindih, sebuah keuntungan yang tidak ada pada exclusion logic murni
  • Kasus tepi (edge case)
    • Jika rentang FOR PORTION OF sepenuhnya berada di dalam satu baris yang ada, hingga 2 baris sisa (depan dan belakang) dibuat
    • Jika tepat cocok dengan batas yang sudah ada, baris sisa tidak diperlukan
  • Baris sisa temporal yang baru dibuat tidak memerlukan hak INSERT, tetapi trigger INSERT yang sudah ada tetap dijalankan → perlu diperhatikan pada audit logging atau fungsi trigger SECURITY DEFINER

Menghapus Riwayat - Erasing History

  • FOR PORTION OF juga bekerja pada DELETE; misalnya menghapus sementara produk tertentu dari katalog pada Juni–Oktober 2025
    • DELETE FROM products FOR PORTION OF valid_at FROM '2025-06-01' TO '2025-10-01' WHERE product_id = 2
  • Hasilnya
    • Periode Juni–Oktober dipotong, dan baris $22.99 yang mencakup [2025-04-01, 2026-01-01) dipecah menjadi dua baris sisa: satu berakhir pada Juni dan satu mulai pada Oktober
    • Data harga sebelum dan sesudah celah dipertahankan dengan nilai aslinya, sehingga DELETE justru menambah jumlah baris
  • Mekanisme pengelolaan tabel temporal menangani semuanya secara otomatis, menghilangkan risiko penghapusan berlebihan atau orphaned fragment di level aplikasi

Kebenaran dalam Iklan - Truth in Advertising

  • Tabel temporal belum lengkap tanpa temporal foreign key, dan Postgres 19 mendukungnya dengan kata kunci PERIOD
    • FOREIGN KEY (product_id, PERIOD valid_at) REFERENCES products (product_id, PERIOD valid_at)
  • Kata kunci PERIOD memberi tahu bahwa foreign key itu sendiri berbasis temporal
    • Product yang dirujuk harus ada selama seluruh periode rentang valid_at milik variant
    • Kombinasi semua baris yang cocok pada tabel yang dirujuk harus sepenuhnya mencakup periode baris yang mereferensikan
  • Percobaan membuat variant yang melampaui rentang waktu product ([2025-01-01, 2027-01-01)) akan ditolak
    • Karena harga Widget hanya didefinisikan hingga pertengahan 2026, variant yang mengklaim berlaku sampai 2027 ditolak karena pelanggaran foreign key constraint
  • Satu batasan penting
    • Temporal foreign key hanya mendukung NO ACTION sebagai referential action; CASCADE, SET NULL, dan SET DEFAULT tidak termasuk
    • Menghapus baris product yang menjadi dependensi variant akan selalu menghasilkan error, karena kompleksitas operasi temporal berantai, sehingga aplikasi harus menanganinya secara eksplisit

Langkah-Langkah Kecil - Baby Steps

  • Fitur yang sudah tersedia saat ini: tabel temporal berbasis valid time dengan pencegahan tumpang tindih, manipulasi data temporal, dan temporal foreign key
  • Kekurangan terbesar adalah system time, yang juga disebut transaction time
    • Valid time melacak kapan sebuah fakta benar di dunia nyata; system time melacak kapan DB mengetahui fakta tersebut, dan banyak sistem memanfaatkan keduanya
    • Area inilah yang diisi oleh ekstensi pg_bitemporal sejak 2015
    • Emulasi system time dengan trigger memang memungkinkan, tetapi berbeda dari pengelolaan transparan oleh engine seperti fitur temporal baru lainnya
  • Dokumentasi tabel temporal juga menyatakan bahwa system time belum didukung secara native dan dapat diemulasi; belum jelas apakah akan hadir di Postgres 20 atau setelahnya, tetapi fondasinya sudah tersedia

Penutup - Final Thoughts

  • Pendekatan EXCLUDE USING gist memang bekerja, tetapi merupakan workaround yang relatif kasar; ekstensi seperti pg_bitemporal telah membuktikan konsep dan menjaga diskusi tetap berjalan
  • Pendekatan ini jauh lebih intuitif dibanding exclusion constraint GiST
    • WITHOUT OVERLAPS pada primary key terbaca seperti bahasa Inggris biasa, dan FOR PORTION OF menggambarkan aksinya secara langsung
    • Pemecahan baris otomatis saat pembaruan dan penghapusan temporal menghilangkan satu kategori potensi bug
  • Perjalanan dari SQL:2011 ke Postgres 19 sangat panjang; Hetti dan komunitas selama bertahun-tahun membuktikan kebutuhan serta kelayakan pola ini, dan kini masuk ke core
  • Dukungan system time pada rilis mendatang perlu diperhatikan; jika Postgres memiliki kedua separuh bi-temporal, kemungkinannya akan berkembang jauh lebih luas

Belum ada komentar.

Belum ada komentar.