7 menit

Transaksi Postgres untuk alur kerja multi-langkah: pola praktis

Pelajari transaksi Postgres untuk alur kerja multi-langkah: cara mengelompokkan pembaruan dengan aman, mencegah penulisan parsial, menangani retry, dan menjaga konsistensi data.

Transaksi Postgres untuk alur kerja multi-langkah: pola praktis

Mengapa pembaruan multi-langkah sering berakhir tidak konsisten

Kebanyakan fitur nyata bukan hanya satu pembaruan basis data. Mereka adalah rangkaian singkat: menyisipkan baris, memperbarui saldo, menandai status, menulis catatan audit, mungkin mengantri pekerjaan. Penulisan parsial terjadi ketika hanya sebagian langkah tersebut yang sampai ke basis data.

Ini muncul ketika sesuatu menginterupsi rangkaian: error server, timeout antara aplikasi dan Postgres, crash setelah langkah ke-2, atau retry yang menjalankan ulang langkah 1. Setiap pernyataan terlihat baik sendiri. Alur kerja rusak ketika berhenti di tengah.

Biasanya Anda bisa melihatnya dengan cepat:

  • Sebuah baris ada, tetapi baris terkait hilang (order dibuat, tapi tidak ada item)
  • Uang berpindah, tapi status tidak berubah (sudah dibayar, masih ditandai belum dibayar)
  • Dua catatan yang seharusnya satu (duplikat subscription setelah retry)
  • Flag yang saling bertentangan (user "aktif" tapi tidak punya plan)
  • Status yang hanya muncul saat beban atau kegagalan

Contoh konkret: upgrade plan memperbarui plan pelanggan, menambahkan catatan pembayaran, dan menambah kredit tersedia. Jika aplikasi crash setelah menyimpan pembayaran tapi sebelum menambah kredit, tim support melihat "dibayar" di satu tabel dan "tidak ada kredit" di tabel lain. Jika klien retry, Anda bahkan bisa merekam pembayaran dua kali.

Tujuannya sederhana: perlakukan alur kerja seperti satu saklar. Entah semua langkah berhasil, atau tidak satupun, sehingga Anda tidak pernah menyimpan pekerjaan setengah jadi.

Transaksi, dalam istilah sederhana

Transaksi adalah cara basis data mengatakan: perlakukan langkah-langkah ini sebagai satu unit kerja. Entah semua perubahan terjadi, atau tidak sama sekali. Ini penting kapan pun alur kerja Anda membutuhkan lebih dari satu pembaruan, seperti membuat baris, memperbarui saldo, dan menulis catatan audit.

Bayangkan memindahkan uang antar dua akun. Anda harus mengurangi dari Akun A dan menambahkan ke Akun B. Jika aplikasi crash setelah langkah pertama, Anda tidak ingin sistem "mengingat" hanya pengurangan.

Commit vs rollback

Saat Anda commit, Anda memberitahu Postgres: simpan semua yang saya lakukan dalam transaksi ini. Semua perubahan menjadi permanen dan terlihat oleh sesi lain.

Saat Anda rollback, Anda memberitahu Postgres: lupakan semua yang saya lakukan dalam transaksi ini. Postgres membatalkan perubahan seolah transaksi tidak pernah terjadi.

Apa yang dijamin Postgres (dan apa yang tidak)

Di dalam transaksi, Postgres menjamin Anda tidak akan mengekspos hasil setengah jadi ke sesi lain sebelum Anda commit. Jika sesuatu gagal dan Anda rollback, basis data membersihkan semua tulis dari transaksi itu.

Transaksi tidak memperbaiki desain alur kerja yang buruk. Jika Anda mengurangi jumlah yang salah, menggunakan user ID yang keliru, atau melewatkan pengecekan yang diperlukan, Postgres akan dengan setia commit hasil yang salah. Transaksi juga tidak otomatis mencegah semua konflik level bisnis (seperti overselling inventori) kecuali Anda mengombinasikannya dengan constraint, lock, atau level isolasi yang tepat.

Alur kerja yang harus digabungkan

Kapan pun Anda memperbarui lebih dari satu tabel (atau lebih dari satu baris) untuk menyelesaikan satu aksi dunia nyata, itu kandidat untuk transaksi. Intinya tetap: entah semuanya selesai, atau tidak ada.

Alur order adalah kasus klasik. Anda mungkin membuat baris order, mereservasi inventori, mengambil pembayaran, lalu menandai order sebagai terbayar. Jika pembayaran berhasil tapi pembaruan status gagal, Anda punya uang yang tertangkap tetapi order tetap terlihat belum dibayar. Jika baris order dibuat tapi stok tidak direservasi, Anda bisa menjual barang yang sebenarnya tidak ada.

Onboarding user juga diam-diam rusak dengan cara yang sama. Membuat user, menyisipkan record profil, menetapkan peran, dan mencatat bahwa email sambutan harus dikirim adalah satu aksi logis. Tanpa pengelompokan, Anda bisa berujung pada user yang bisa masuk tapi tidak punya izin, atau profil yang ada tanpa user.

Tindakan back-office sering membutuhkan perilaku "jejak kertas + perubahan status" yang ketat. Menyetujui permintaan, menulis entri audit, dan memperbarui saldo harus berhasil bersama. Jika saldo berubah tapi log audit hilang, Anda kehilangan bukti siapa yang mengubah apa dan mengapa.

Pekerjaan background juga mendapat manfaat, terutama saat Anda memproses item kerja dengan beberapa langkah: klaim item sehingga dua worker tidak mengerjakannya, terapkan pembaruan bisnis, catat hasil untuk pelaporan dan retry, lalu tandai item selesai (atau gagal dengan alasan). Jika langkah-langkah itu terpisah, retry dan concurrency membuat kekacauan.

Rancang alur kerja sebelum menulis SQL

Fitur multi-langkah rusak ketika Anda memperlakukannya seperti tumpukan pembaruan independen. Sebelum membuka klien basis data, tulis alur kerja sebagai cerita pendek dengan satu garis finish yang jelas: apa yang tepatnya dihitung sebagai "selesai" untuk pengguna?

Mulai dengan daftar langkah dalam bahasa biasa, lalu definisikan satu kondisi sukses. Contoh: "Order dibuat, inventori direservasi, dan pengguna melihat nomor konfirmasi order." Apa pun di bawah itu bukan sukses, meskipun beberapa tabel diperbarui.

Selanjutnya, gambarkan batas tegas antara pekerjaan basis data dan pekerjaan eksternal. Langkah basis data adalah yang bisa Anda lindungi dengan transaksi. Panggilan eksternal seperti pembayaran kartu, pengiriman email, atau pemanggilan API pihak ketiga bisa gagal dengan cara lambat dan tak terduga, dan biasanya tidak bisa Anda rollback.

Pendekatan perencanaan sederhana: pisahkan langkah menjadi (1) harus all-or-nothing, (2) bisa terjadi setelah commit.

Putuskan apa yang masuk ke dalam transaksi

Di dalam transaksi, simpan hanya langkah yang harus konsisten bersama:

  • Membuat atau memperbarui baris inti (order, invoice, saldo akun)
  • Mereservasi sumber daya bersama (inventori, kursi, kuota)
  • Mencatat event tahan lama "apa yang harus dilakukan selanjutnya" (tabel outbox)
  • Menegakkan aturan dengan constraint (kunci unik, foreign key)

Pindahkan efek samping ke luar. Misalnya, commit order dulu, lalu kirim email konfirmasi berdasarkan catatan outbox.

Tulis ekspektasi rollback per langkah

Untuk setiap langkah, tulis apa yang harus terjadi jika langkah berikutnya gagal. "Rollback" bisa berarti rollback basis data, atau bisa berarti aksi kompensasi.

Contoh: jika pembayaran berhasil tapi reservasi inventori gagal, putuskan sebelumnya apakah Anda akan langsung refund, atau menandai order sebagai "pembayaran tercapture, menunggu stok" dan menanganinya secara asinkron.

Langkah demi langkah: membungkus alur kerja dalam transaksi

Transaksi memberi tahu Postgres: perlakukan langkah-langkah ini sebagai satu unit. Entah semua terjadi, atau tidak sama sekali. Itulah cara paling sederhana untuk mencegah penulisan parsial.

Alur dasar

Gunakan satu koneksi basis data (satu sesi) dari awal sampai akhir. Jika Anda menyebarkan langkah ke koneksi berbeda, Postgres tidak bisa menjamin hasil all-or-nothing.

Runtutan sederhana: begin, jalankan baca dan tulis yang diperlukan, commit jika semuanya sukses, kalau tidak rollback dan kembalikan error yang jelas.

Berikut contoh minimal dalam SQL:

BEGIN;

-- reads that inform your decision
SELECT balance FROM accounts WHERE id = 42 FOR UPDATE;

-- writes that must stay together
UPDATE accounts SET balance = balance - 50 WHERE id = 42;
INSERT INTO ledger(account_id, amount, note) VALUES (42, -50, 'Purchase');

COMMIT;

-- on error (in code), run:
-- ROLLBACK;

Jaga agar singkat (dan mudah di-debug)

Transaksi menahan lock selama berjalan. Semakin lama Anda membiarkannya terbuka, semakin Anda memblokir pekerjaan lain dan semakin mungkin menghadapi timeout atau deadlock. Lakukan yang penting di dalam transaksi, dan pindahkan tugas lambat (mengirim email, memanggil provider pembayaran, menghasilkan PDF) ke luar.

Saat sesuatu gagal, log konteks yang cukup untuk mereproduksi masalah tanpa membocorkan data sensitif: nama alur kerja, order_id atau user_id, parameter kunci (jumlah, mata uang), dan kode error Postgres. Hindari melog payload penuh, data kartu, atau detail pribadi.

Dasar-dasar konkurensi: penguncian dan isolasi tanpa jargon

Implementasikan pembungkus transaksi
Buat API Go dengan Postgres dan terapkan transaksi pendek dengan jalur kesalahan yang jelas.

Konkurensi hanyalah dua hal yang terjadi pada saat bersamaan. Bayangkan dua pelanggan mencoba membeli tiket konser terakhir. Kedua layar menunjukkan "1 tersisa," keduanya klik Bayar, dan sekarang aplikasi Anda harus memutuskan siapa yang mendapatkannya.

Tanpa perlindungan, kedua permintaan bisa membaca nilai lama yang sama dan keduanya menulis pembaruan. Begitulah Anda berakhir dengan inventori negatif, reservasi duplikat, atau pembayaran tanpa order.

Lock baris adalah pengaman paling sederhana. Anda mengunci baris spesifik yang akan diubah, melakukan pengecekan, lalu memperbaruinya. Transaksi lain yang menyentuh baris sama harus menunggu sampai Anda commit atau rollback, yang mencegah pembaruan ganda.

Pola umum: mulai transaksi, pilih baris inventori dengan FOR UPDATE, verifikasi ada stok, kurangi jumlahnya, lalu insert order. Itu seperti "menahan pintu" saat Anda menyelesaikan langkah kritis.

Level isolasi mengatur seberapa banyak keanehan dari transaksi paralel yang Anda izinkan. Trade-off biasanya antara keamanan dan kecepatan:

  • Read Committed (default): cepat, tapi Anda bisa melihat perubahan yang di-commit oleh orang lain antar pernyataan.
  • Repeatable Read: transaksi melihat snapshot stabil, bagus untuk pembacaan konsisten, mungkin menyebabkan lebih banyak retry.
  • Serializable: keamanan terkuat, Postgres mungkin membatalkan satu transaksi untuk menjaga hasil seolah-olah dijalankan satu-per-satu.

Jaga lock tetap singkat. Jika transaksi dibiarkan terbuka saat Anda memanggil API eksternal atau menunggu aksi pengguna, Anda akan menyebabkan tungguan panjang dan timeout. Lebih baik punya jalur kegagalan yang jelas: set lock timeout, tangkap error, dan kembalikan "silakan ulangi" daripada membiarkan permintaan menggantung.

Jika Anda perlu melakukan kerja di luar basis data (seperti mengenakan biaya kartu), pisahkan alur: reservasi cepat, commit, lalu lakukan bagian lambat, dan finalisasi dengan transaksi pendek lain.

Retry yang tidak menciptakan duplikat

Retry adalah hal yang normal di aplikasi yang memakai Postgres. Permintaan bisa gagal meskipun kode Anda benar: deadlock, statement timeout, putus jaringan singkat, atau serialization error di level isolasi lebih tinggi. Jika Anda hanya menjalankan ulang handler yang sama, Anda berisiko membuat order kedua, mengenakan biaya dua kali, atau menyisipkan baris "event" duplikat.

Solusinya adalah idempotensi: operasi harus aman dijalankan dua kali dengan input yang sama. Basis data harus bisa mengenali "ini permintaan yang sama" dan merespons konsisten.

Pola praktis adalah menambahkan idempotency key (sering request_id yang dibuat klien) ke setiap alur multi-langkah dan menyimpannya di record utama, lalu menambahkan unique constraint pada key itu.

Misalnya: di checkout, buat request_id saat pengguna klik Bayar, lalu insert order dengan request_id itu. Jika retry terjadi, upaya kedua melanggar constraint unik dan Anda mengembalikan order yang sudah ada alih-alih membuat yang baru.

Yang biasanya penting:

  • Gunakan constraint unik pada (request_id) atau (user_id, request_id) untuk memblok duplikat.
  • Saat constraint terpenuhi, ambil baris yang ada dan kembalikan hasil yang sama.
  • Buat efek samping mengikuti aturan yang sama: hanya satu payment intent per order, hanya satu event "order confirmed" per order.
  • Log request_id agar tim support bisa menelusuri apa yang terjadi.

Jaga loop retry di luar transaksi. Setiap upaya harus memulai transaksi baru dan menjalankan ulang unit kerja dari atas. Retry di dalam transaksi yang gagal tidak membantu karena Postgres menandai transaksi sebagai aborted.

Satu contoh kecil: aplikasi Anda mencoba membuat order dan mereservasi inventori, tetapi mengalami timeout tepat setelah COMMIT. Klien retry. Dengan idempotency key, permintaan kedua mengembalikan order yang sudah dibuat dan melewatkan reservasi kedua alih-alih menggandakan pekerjaan.

Gunakan basis data untuk menegakkan aturan, bukan hanya kode Anda

Pindahkan efek samping keluar
Modelkan event outbox dan background job sehingga efek samping terjadi setelah commit.

Transaksi menjaga alur kerja multi-langkah bersama, tapi mereka tidak otomatis membuat data benar. Cara kuat untuk menghindari dampak penulisan parsial adalah membuat keadaan "salah" menjadi sulit atau tidak mungkin di basis data, bahkan jika ada bug di kode aplikasi.

Mulai dengan pengaman dasar. Foreign key memastikan referensi nyata (order line tidak bisa menunjuk ke order yang hilang). NOT NULL menghentikan baris setengah terisi. CHECK constraint menangkap nilai yang tidak masuk akal (misalnya quantity > 0, total_cents >= 0). Aturan-aturan ini berjalan di setiap tulis, tidak peduli layanan atau skrip mana yang menyentuh basis data.

Untuk alur kerja yang lebih panjang, modelkan perubahan status secara eksplisit. Daripada banyak flag boolean, gunakan satu kolom status (pending, paid, shipped, canceled) dan hanya izinkan transisi valid. Anda bisa menegakkannya dengan constraint atau trigger sehingga basis data menolak lompatan ilegal seperti shipped -> pending.

Unikitas adalah bentuk kebenaran lain. Tambahkan constraint unik di tempat duplikat akan merusak alur Anda: order_number, invoice_number, atau idempotency_key yang digunakan untuk retry. Lalu, jika aplikasi Anda mengulangi permintaan yang sama, Postgres memblok insert kedua dan Anda bisa aman mengembalikan "sudah diproses" daripada membuat order duplikat.

Saat Anda membutuhkan keterlacakan, simpan itu secara eksplisit. Tabel audit (atau history) yang merekam siapa mengubah apa, dan kapan, mengubah "update misterius" menjadi fakta yang bisa Anda query saat insiden.

Kesalahan umum yang menyebabkan penulisan parsial

Kebanyakan penulisan parsial bukan disebabkan oleh "SQL yang buruk." Mereka datang dari keputusan alur kerja yang membuatnya mudah untuk commit hanya setengah cerita.

Perangkap yang muncul di aplikasi nyata

  • Melakukan pekerjaan eksternal yang lambat saat transaksi terbuka. Memanggil provider pembayaran, mengirim email, atau mengunggah file di dalam transaksi membuat lock ditahan lebih lama dari yang diperlukan. Jika API lambat atau timeout, pengguna lain antre di belakang transaksi terbuka Anda.
  • Membaca di luar transaksi, lalu menulis nanti. Contoh: Anda mengambil saldo user, menampilkannya di layar, lalu nanti menguranginya berdasarkan nilai lama itu. Sesi lain mungkin sudah mengubah saldo di antaranya.
  • Menangkap error tapi tetap melakukan commit sesuatu. Pola umum adalah "coba langkah 1, coba langkah 2, log error, kembalikan sukses saja." Jika kode sampai pada COMMIT setelah kegagalan, Anda baru saja membuat basis data tidak konsisten dengan sengaja.
  • Memperbarui tabel dalam urutan berbeda di jalur kode yang berbeda. Jika satu permintaan memperbarui accounts lalu orders, tapi lainnya memperbarui orders lalu accounts, Anda meningkatkan kemungkinan deadlock saat beban tinggi.
  • Membiarkan transaksi terbuka terlalu lama. Transaksi panjang dapat memblok write, menunda pembersihan vacuum, dan membuat timeout yang membingungkan.

Contoh konkret: di checkout, Anda mereservasi inventori, membuat order, lalu mengenakan biaya kartu. Jika Anda mengenakan biaya di dalam transaksi yang sama, Anda mungkin menahan lock inventori sambil menunggu jaringan. Jika biaya berhasil tapi transaksi Anda kemudian rollback, Anda sudah mengenakan biaya pelanggan tanpa order.

Pola yang lebih aman: fokuskan transaksi pada state basis data (reserve inventori, buat order, catat payment pending), commit, lalu panggil API eksternal, lalu tulis hasilnya di transaksi pendek baru. Banyak tim mengimplementasikannya dengan status pending sederhana dan pekerjaan background.

Checklist cepat untuk operasi all-or-nothing

Saat sebuah alur kerja punya banyak langkah (insert, update, charge, send), tujuannya sederhana: entah semuanya tercatat, atau tidak sama sekali.

Batas transaksi

Simpan semua penulisan basis data yang diperlukan dalam satu transaksi. Jika satu langkah gagal, rollback dan kembalikan data persis seperti semula.

Buat kondisi sukses eksplisit. Contoh: "Order dibuat, stok direservasi, dan status pembayaran tercatat." Apa pun selain itu adalah jalur kegagalan yang harus membatalkan transaksi.

  • Semua penulisan yang diperlukan terjadi di dalam satu blok BEGIN ... COMMIT.
  • Ada satu status selesai yang jelas di basis data (bukan hanya di memori aplikasi).
  • Setiap error menyebabkan ROLLBACK, dan pemanggil mendapat hasil kegagalan yang jelas.

Pengaman (agar retry tidak merugikan)

Asumsikan permintaan yang sama bisa di-retry. Basis data harus membantu menegakkan aturan hanya-sekali.

  • Dukung aksi hanya-sekali dengan constraint unik (satu record pembayaran per order, atau satu reservasi per item per order).
  • Buat retry aman dan dapat diulang (input yang sama memberikan keadaan akhir yang sama, bukan duplikat).

Jaga transaksi tetap singkat

Lakukan pekerjaan minimal di dalam transaksi, dan hindari menunggu panggilan jaringan sambil menahan lock.

  • Singkatkan transaksi, dan atur timeout supaya tidak menggantung.
  • Kerja lambat (memanggil provider pembayaran) lakukan di luar transaksi, lalu catat hasilnya di transaksi pendek baru.

Amati kegagalan

Jika Anda tidak bisa melihat di mana ia rusak, Anda akan terus menebak.

  • Log langkah alur dan request id untuk setiap kegagalan.
  • Pantau rasio rollback dan lock timeout agar Anda menangkap risiko penulisan parsial lebih awal.

Contoh: alur checkout yang tetap konsisten saat gagal

Mulai dengan constraint bawaan
Hasilkan skema, constraint, dan handler bersama sehingga penulisan parsial lebih sulit terjadi.

Checkout memiliki beberapa langkah yang harus bergerak bersama: buat order, reservasi inventori, catat percobaan pembayaran, lalu tandai status order.

Bayangkan pengguna klik Beli untuk 1 item.

Alur aman (pekerjaan DB sebagai satu unit)

Di dalam satu transaksi, lakukan hanya perubahan basis data:

  • Insert baris orders dengan status pending_payment.
  • Reservasi stok (misalnya, kurangi inventory.available atau buat baris reservations).
  • Insert baris payment_intents dengan idempotency_key yang disediakan klien (unik).
  • Insert baris outbox seperti "order_created".

Jika salah satu pernyataan gagal (kehabisan stok, error constraint, crash), Postgres akan rollback seluruh transaksi. Anda tidak akan berakhir dengan order tanpa reservasi, atau reservasi tanpa order.

Bagaimana jika pembayaran gagal di tengah?

Provider pembayaran ada di luar basis data, jadi perlakukan itu sebagai langkah terpisah.

Jika panggilan provider gagal sebelum Anda commit, batalkan transaksi dan tidak ada yang tertulis. Jika panggilan provider gagal setelah Anda commit, jalankan transaksi baru yang menandai percobaan pembayaran sebagai gagal, melepaskan reservasi, dan mengatur status order menjadi canceled.

Retry tanpa membuat order kedua

Minta klien mengirim idempotency_key per upaya checkout. Tegakkan dengan indeks unik pada payment_intents(idempotency_key) (atau pada orders jika Anda lebih suka). Saat retry, kode Anda mencari baris yang ada dan melanjutkan alih-alih menyisipkan order baru.

Email dan notifikasi

Jangan kirim email di dalam transaksi. Tuliskan record outbox dalam transaksi yang sama, lalu biarkan worker background mengirim email setelah commit. Dengan begitu Anda tidak pernah mengirim email untuk order yang kemudian di-rollback.

Langkah selanjutnya: terapkan ini pada satu alur minggu ini

Pilih satu alur kerja yang menyentuh lebih dari satu tabel: signup + enqueue email sambutan, checkout + inventori, invoice + entri ledger, atau buat proyek + pengaturan default.

Tulis langkah-langkahnya dulu, lalu tulis aturan yang harus selalu benar (invariant). Contoh: "Order semua-terbayar dan tereservasi, atau tidak terbayar dan tidak tereservasi. Jangan pernah setengah tereservasi." Ubah aturan itu menjadi unit all-or-nothing.

Rencana sederhana:

  • Daftar operasi SQL persis dalam urutan (baca, insert, update, delete).
  • Tambahkan constraint basis data yang kurang dulu (kunci unik, foreign key, check constraint).
  • Tambahkan idempotency key untuk permintaan sehingga retry tidak membuat duplikat.
  • Bungkus langkah-langkah dalam satu transaksi dan buat titik sukses eksplisit (commit hanya saat semua pengecekan lolos).
  • Tentukan seperti apa retry yang aman (idempotency key sama, hasil sama).

Lalu uji kasus buruk dengan sengaja. Simulasikan crash setelah langkah ke-2, timeout tepat sebelum commit, dan double-submit dari UI. Tujuannya adalah hasil yang membosankan: tidak ada baris yatim, tidak ada double charge, tidak ada status pending selamanya.

Jika Anda sedang membuat prototipe cepat, membantu untuk menggambar alur di alat perencanaan sebelum menghasilkan handler dan skema. Misalnya, Koder.ai (koder.ai) memiliki Planning Mode dan mendukung snapshot serta rollback, yang bisa berguna saat Anda iterasi batas transaksi dan constraint.

Lakukan ini untuk satu alur minggu ini. Yang kedua akan jauh lebih cepat.

Pertanyaan umum

Kapan saya sebaiknya menggunakan transaksi Postgres?

Gunakan transaksi ketika satu tindakan pengguna mengubah beberapa baris atau tabel yang harus tetap konsisten. Contoh umum mencakup checkout, transfer rekening, pendaftaran dengan pemberian peran, dan reservasi inventaris.

Apa fungsi COMMIT di Postgres?

Commit membuat setiap perubahan dalam transaksi menjadi permanen dan terlihat oleh sesi database lain. Lakukan commit hanya setelah setiap pemeriksaan dan penulisan yang diperlukan berhasil.

Apa yang terjadi saat saya melakukan ROLLBACK?

Rollback membatalkan transaksi dan menghapus perubahan yang belum di-commit. Kode Anda harus melakukan rollback jika langkah wajib apa pun gagal, lalu mengembalikan error yang dapat ditangani pemanggil.

Apakah semua kueri transaksi harus menggunakan koneksi yang sama?

Simpan pembacaan dan penulisan database untuk alur kerja tersebut pada koneksi database yang sama, dari BEGIN hingga COMMIT atau ROLLBACK. Koneksi terpisah membuat transaksi terpisah.

Haruskah saya memanggil API pembayaran atau email di dalam transaksi?

Tidak. Transaksi menjaga perubahan database tetap bersama, tetapi tidak membatalkan tagihan kartu, email, atau panggilan API pihak ketiga. Commit status database Anda terlebih dahulu, catat peristiwa outbox atau status tertunda, lalu jalankan pekerjaan eksternal secara terpisah.

Bagaimana cara mencegah dua pengguna membeli barang terakhir?

Kunci baris sebelum memeriksa dan memperbarui nilai bersama, seperti inventaris yang tersedia. Misalnya, pilih baris inventaris dengan FOR UPDATE, verifikasi stok, kurangi jumlahnya, dan buat pesanan sebelum melakukan commit.

Bagaimana percobaan ulang dapat menghindari pesanan duplikat?

Beri setiap permintaan kunci idempotensi yang stabil dan tegakkan dengan constraint unik. Saat percobaan ulang mengenai constraint tersebut, muat dan kembalikan hasil yang sudah ada alih-alih memasukkan pesanan atau catatan pembayaran lain.

Mengapa percobaan ulang harus memulai transaksi baru?

Lakukan percobaan ulang dari awal dalam transaksi baru. Setelah terjadi error pernyataan, Postgres menandai transaksi saat ini sebagai dibatalkan, sehingga transaksi itu tidak dapat dilanjutkan dengan aman sampai Anda melakukan rollback.

Constraint database mana yang membantu mencegah data tidak konsisten?

Gunakan foreign key untuk referensi yang valid, NOT NULL untuk kolom wajib, constraint CHECK untuk nilai yang valid, dan constraint unik untuk catatan yang hanya boleh ada satu kali. Aturan ini melindungi data bahkan jika kode aplikasi memiliki bug.

Mengapa transaksi harus tetap singkat?

Jaga transaksi tetap berfokus pada pekerjaan database yang cepat. Transaksi panjang menahan kunci, meningkatkan waktu tunggu, dan dapat menyebabkan deadlock atau timeout, terutama jika menunggu panggilan jaringan.

Related posts