- soft delete berbasis kolom
archived_at sering digunakan untuk pemulihan data dan kepatuhan regulasi, tetapi seiring waktu kompleksitas dan inefisiensi meningkat
- Pendekatan ini membuat query, indeks, migrasi, dan logika pemulihan menjadi lebih rumit, dan karena sebagian besar data arsip tidak pernah dibaca lagi, hal ini menimbulkan beban yang tidak perlu pada database
- Sebagai alternatif, diajukan arsip berbasis event aplikasi, arsip berbasis trigger, dan arsip berbasis WAL (Change Data Capture)
- Setiap pendekatan berbeda dalam kompleksitas operasional, kebutuhan infrastruktur, dan kemudahan pemulihan, dan khususnya pendekatan berbasis WAL memerlukan integrasi dengan sistem eksternal seperti Kafka
- Untuk proyek baru, pendekatan berbasis trigger adalah pilihan paling seimbang dari sisi kesederhanaan dan kemudahan pemeliharaan
Masalah pada soft delete
- Umumnya data dihapus secara logis menggunakan kolom boolean
deleted atau timestamp archived_at
- Data bisa dipulihkan jika pelanggan tidak sengaja menghapusnya
- Dalam beberapa kasus, data juga perlu disimpan untuk tujuan regulasi atau audit
- Namun, kolom
archived_at menimbulkan kompleksitas di seluruh query, operasi, dan kode aplikasi
- Sebagian besar data arsip tidak pernah dibaca lagi
- Karena masalah perilaku API atau alat otomasi seperti Terraform, jutaan baris yang tidak perlu bisa menumpuk
- Jika pekerjaan pembersihan data arsip tidak dikonfigurasi, akan terjadi penurunan performa saat backup dan restore database
- Data arsip harus difilter dalam query dan indeks, sehingga ada risiko kebocoran data
- Saat migrasi, menangani data lama atau mengubah nilai default menjadi sulit
- Logika pemulihan menjadi rumit, dan bug bisa muncul jika diperlukan pemanggilan sistem eksternal
- Akibatnya, pendekatan
archived_at tampak sederhana, tetapi dalam jangka panjang biaya pemeliharaannya tinggi
Arsip di level aplikasi
- Saat penghapusan, aplikasi menerbitkan event, mengirimkannya ke SQS, lalu layanan lain mengarsipkannya ke S3
- Kelebihan
- Menyederhanakan database utama dan kode aplikasi
- Pembersihan resource eksternal dapat diproses secara asinkron, meningkatkan performa dan keandalan
- Dapat diserialisasi dalam bentuk JSON sehingga tersimpan dalam struktur yang ramah aplikasi
- Kekurangan
- Bug pada kode aplikasi dapat menyebabkan hilangnya data arsip
- Kompleksitas infrastruktur operasional meningkat karena message queue dan komponen lain
- Data arsip di S3 memerlukan alat pencarian dan pemulihan
Arsip berbasis trigger
- Trigger sebelum penghapusan menyalin baris ke tabel archive terpisah dalam bentuk JSON
- Contoh tabel:
archive(id, table_name, record_id, data, archived_at, caused_by_table, caused_by_id)
- Saat foreign key delete (cascade), digunakan variabel sesi (
archive.cause_table, archive.cause_id) untuk melacak penyebab penghapusan
- Memungkinkan penelusuran record induk mana yang menghapus data turunan
- Kelebihan
- Tabel live tetap bersih, tanpa perlu kolom
archived_at
- Pembersihan tabel arsip (
WHERE archived_at < NOW() - INTERVAL '90 days') sederhana
- Efisiensi query dan indeks tetap terjaga, migrasi lebih sederhana
- Ukuran backup berkurang
- Tabel arsip dapat dikelola dengan tablespace terpisah atau partisi berbasis waktu
Arsip berbasis WAL (Change Data Capture)
- Log WAL PostgreSQL dibaca untuk men-stream event penghapusan ke sistem eksternal
- Alat yang umum: Debezium (terintegrasi dengan Kafka)
- Contoh alur:
PostgreSQL → Debezium → Kafka → Consumer → Archive Storage
- Alternatif yang lebih ringan
- pgstream: mengirim WAL langsung ke webhook atau message queue
- wal2json: mengeluarkan WAL dalam format JSON
- pg_recvlogical: alat logical replication bawaan PostgreSQL
- Kompleksitas operasional
- Pendekatan berbasis Kafka memerlukan monitoring, penanganan gangguan, dan tuning
- Jika consumer tertunda, dapat terjadi penumpukan file WAL → risiko kehabisan ruang disk
- Ini dapat dibatasi dengan pengaturan
max_slot_wal_keep_size pada PostgreSQL 13+
- Monitoring dan alert untuk keterlambatan replication slot wajib ada
- Kelebihan
- Dapat menangkap semua perubahan tanpa memodifikasi kode aplikasi
- Dapat di-stream ke berbagai tujuan (S3, data warehouse, indeks pencarian)
- Tidak menambah beban pada database utama
- Kekurangan
- Kompleksitas operasional dan biaya infrastruktur tinggi
- Jika consumer tertunda, bisa terjadi kehilangan data atau perlu sinkronisasi ulang
- Saat skema berubah, diperlukan koordinasi antara source dan consumer
Ide replika yang tidak memproses penghapusan
- Diajukan ide untuk mempertahankan replika PostgreSQL yang mengabaikan query DELETE
- Semua data yang tidak dihapus dapat terus diakumulasikan sebagai arsip
- Data arsip bisa di-query langsung
- Potensi masalah
- Mungkin sulit membedakan informasi penghapusan
- Ada risiko konflik saat migrasi diterapkan
- Biaya penyimpanan dan operasional meningkat
Kesimpulan
- Untuk proyek baru, arsip berbasis trigger adalah pilihan yang paling praktis
- Konfigurasinya sederhana dan menjaga tabel live tetap bersih
- Tanpa infrastruktur tambahan, data arsip tetap mudah diakses dan dikelola
- Jika infrastruktur kompleks sudah tersedia atau diperlukan streaming ke banyak tujuan, maka pendekatan berbasis WAL lebih cocok
4 komentar
Kalau berbasis trigger, bukannya saya belajar itu akan membebani DB...? Malah merekomendasikan trigger ya
Jika beban yang muncul dari trigger sebesar itu saja sudah jadi masalah, maka bahkan tanpa trigger pun situasinya memang sudah penuh masalah.
Pada akhirnya, regulasi itu memang selalu menimbulkan biaya. Yah, bagaimanapun juga itu adalah bagian yang harus ditanggung konsumen.
Komentar Hacker News
Di domain perbankan tempat saya bekerja, saya justru merasa soft delete lebih menguntungkan
Jika ada kolom
deleted_at, penulisan kueri jadi jelas, dan kueri analitik maupun halaman admin bisa menangani dataset yang samaPenghapusan dalam banyak kasus jarang terjadi, dan baris yang di-soft delete juga hampir tidak pernah menimbulkan masalah performa
Selain itu, karena relasi tetap terjaga, pemulihan (undo) juga sederhana
Saya bahkan lebih suka membuat baris benar-benar immutable dan menambahkan baris baru saat update
Untuk meninggalkan log, saya rasa pendekatan yang baik adalah memakai trigger DB agar saat INSERT/UPDATE/DELETE terjadi, catatan ditulis ke tabel salinan
Pada tabel yang pernah saya lihat, ketika 50~70% isinya sudah di-soft delete, penurunan performa memang jelas terlihat
Pada akhirnya soft delete bergantung pada konteks, dan perlu analisis awal
Dalam kebanyakan kasus ini tidak perlu, tapi bisa membantu menghemat RAM
Solusi yang sebenarnya adalah Event Sourcing, yaitu mencatat semua perubahan sebagai event
Performanya memang lebih rendah, tapi bisa ditutupi dengan snapshot dan sinkronisasi (sync)
Dengan fitur time travel, status masa lalu bisa ditelusuri secara utuh
Status terbaru ada di baris dengan timestamp terbesar, dan status masa lalu bisa dilihat lewat filter
Pendekatan ini memungkinkan pengelolaan riwayat yang kuat
Jebakan terbesar soft delete adalah kompleksitas kueri
Awalnya terasa cukup dengan menambahkan
WHERE deleted_at IS NULL, tapi beberapa bulan kemudian data hantu muncul di laporan karena filter terlewatIni bisa diatasi dengan View, tetapi pada akhirnya kita tetap harus mempertahankan pola akses paralel, dan saat perlu melihat data yang sudah dihapus, abstraksinya harus dilewati
Event sourcing lebih bersih, tetapi karena beban operasional-nya besar, kebanyakan orang memilih pendekatan hibrida
Masalahnya, banyak SWE dan engineer BI tidak terbiasa dengan SQL dan desain skema
Masalah yang bahkan lebih umum daripada soft delete adalah penanganan Type 2 Slowly Changing Dimension
Kebanyakan orang membuat audit table yang sebenarnya tidak perlu, lalu mengulang UPDATE/INSERT yang tidak efisien
Padahal DB itu sistem yang sangat indah, dan sayangnya tidak cukup dihargai
Saya pikir akan bagus jika soft delete tersedia sebagai fitur bawaan DB
Akan ideal jika bisa diaktifkan per tabel dan strategi penanganan penghapusannya dapat dipilih
Namun banyak tim akhirnya tetap mengimplementasikannya dengan pendekatan SCD (Slowly Changing Dimension) karena kebutuhan khusus
Dalam pengalaman saya, pendekatan berbasis trigger adalah yang paling stabil
Tabel arsip sebaiknya dipertahankan sebagai append-only, dan pemulihan ditangani di layer aplikasi
Update diperlakukan sebagai soft delete, dan trigger menangkap status sebelumnya
Trigger harus dijalankan pada tahap BEFORE, dan logikanya harus sederhana
Partisi bulanan adalah yang umum, dan jika beban tulis tinggi, lebih baik dibagi harian
Saya ingin DB berevolusi dari stateful → stateless
Saya lebih suka struktur yang mencatat semua perubahan sebagai event append-only, lalu mengekspresikan data yang dibutuhkan lewat view
Akan ideal jika DB otomatis mengelola materialized index
Beberapa DB modern memang menyediakan fitur seperti ini, tetapi perkembangan yang berfokus pada OLTP masih kurang
Penjelasan Martin Fowler layak dijadikan referensi
Di perusahaan lama saya, soft delete diterapkan di semua sistem
Saya masih ingat profesor saya pernah berkata, “di dunia bisnis, data tidak pernah benar-benar dihapus”
Ruang penyimpanan itu murah, jadi data seharusnya jangan pernah dihapus
Database adalah tempat menyimpan fakta (fact)
Pembuatan record adalah fakta baru, penghapusan juga fakta lain
Tetapi jika baris dihapus secara fisik, maka fakta itu hilang
Dalam banyak kasus, penghapusan seperti ini tidak diinginkan
Biaya retensi dan risiko keamanan harus dipertimbangkan
Keputusan untuk menyimpan data secara permanen harus dipikirkan dengan matang
Untuk itu, memahami siklus hidup data adalah hal yang penting
Di Firezone, awalnya soft delete dipakai untuk audit log, tetapi akhirnya ditinggalkan karena masalah migrasi
Sebagai gantinya, mereka beralih memakai Postgres CDC (Change Data Capture) untuk mengekspor event ke tabel terpisah yang dioptimalkan untuk penulisan
Menurut saya soft delete berguna untuk fitur pemulihan pengguna, tetapi tidak cocok untuk audit atau kepatuhan
Membuat View di atas tabel yang punya field soft delete untuk menyembunyikan baris yang sudah dihapus adalah pendekatan yang rapi
Dengan begitu aplikasi tidak perlu peduli apakah data sudah dihapus atau belum
Aplikasi tetap melakukan baca/tulis/hapus ke tabel yang sama
Ada pertanyaan tentang bagaimana menangani schema drift
Jika ingin memulihkan data yang diserialisasi dengan skema saat data itu dihapus, perubahan skema akan menjadi masalah
Kebanyakan pemulihan terjadi hanya dalam beberapa hari setelah penghapusan, jadi dampak perubahan skema kecil
Memigrasikan arsip lama ke model baru adalah pekerjaan yang rumit dan rawan kesalahan
Pada akhirnya strategi pendekatannya berbeda tergantung bagaimana sistem itu digunakan