Topik wawancara representatif

Bagaimana Cara Melakukan Migrasi Skema PostgreSQL Tanpa Downtime?

BackendSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Sebuah tabel users PostgreSQL memiliki 200 juta baris, dan 80 instance aplikasi di-roll out selama 30 menit. Migrasikan kolom username yang unik dan non-null ke handle tanpa menghentikan proses write atau menjadwalkan maintenance window. Bagaimana Anda merancang perubahan skema, rilis aplikasi, backfill, validasi, cutover, dan rollback?

Masalah dan Cakupan

Sebuah layanan pengguna (user service) berjalan di PostgreSQL, dan tabel users miliknya berisi 200 juta baris. username TEXT NOT NULL memiliki indeks unik. Setiap request online, background job, dan event consumer saat ini membaca dan menulis ke kolom tersebut. Tim ingin mengubah nama field tersebut menjadi handle; selama migrasi, kedua kolom merepresentasikan nilai bisnis yang persis sama.

Layanan ini memiliki 80 instance aplikasi, dan rolling deployment membutuhkan waktu 30 menit. Background workers mungkin me-restart lebih lambat daripada instance web. Trafik normal tidak boleh dihentikan, dan tidak ada maintenance window. SLO yang ada tetap berlaku: migrasi harus diperlambat (throttle) atau dihentikan jika write p99, waktu tunggu lock (lock waits), replication lag, atau beban database melampaui batas (threshold) produksi. Tujuannya adalah menyelesaikan penggantian nama sambil memberikan setiap fase invariant yang eksplisit, entry gate, exit gate, dan jalur rollback.

200 juta baris, 80 instance, dan rollout 30 menit adalah asumsi wawancara. "Zero downtime" berarti tidak ada pemadaman terencana sambil tetap melindungi SLO yang ada; ini bukan berarti mengabaikan lock singkat, percobaan yang gagal, atau throttling. Keterampilan intinya adalah membagi perubahan database menjadi beberapa rilis yang kompatibel ke depan dan ke belakang (forward- and backward-compatible), jadi ini adalah pertanyaan backend. Sharding, migrasi lintas database, dan konflik multi-primary berada di luar cakupan tahap pertama.

Apa yang Dinilai oleh Pewawancara

Sinyal pertama adalah apakah kandidat mengenali masalah kompatibilitas. Jika tim langsung menjalankan RENAME COLUMN username TO handle, instance lama masih merujuk ke username sementara instance baru hanya mengetahui handle. Setidaknya satu versi akan gagal selama jendela 30 menit versi campuran tersebut. Perintah database yang cepat tidak serta-merta membuat rollout aplikasi menjadi zero-downtime.

Sinyal kedua adalah pemisahan antara migrasi skema, migrasi data, dan code cutover. Desain yang aman biasanya mengikuti pola expand-and-contract: tambahkan struktur yang dapat diabaikan oleh kode lama, deploy kode yang kompatibel, lakukan backfill dan validasi secara bertahap (batch), alihkan operasi read dan write, dan baru setelah itu hapus struktur lama. Setiap langkah harus dapat di-deploy, diobservasi, dan diulang (retryable) secara independen. Update 200 juta baris dan DDL destruktif tidak boleh digabungkan dalam satu transaksi deployment.

Sinyal ketiga adalah memahami batasan lock dan scan pada PostgreSQL. Subcommand ALTER TABLE yang berbeda memerlukan lock yang berbeda, dan PostgreSQL mengambil ACCESS EXCLUSIVE kecuali dokumentasi menyatakan sebaliknya. Menambahkan kolom yang nullable tanpa nilai default tidak menulis ulang tabel, tetapi tetap membutuhkan lock. Jika permintaan lock tersebut mengantre di belakang transaksi yang berjalan lama, permintaan berikutnya dapat ikut mengantre di belakangnya. NOT VALID, VALIDATE CONSTRAINT, dan CREATE INDEX CONCURRENTLY mengurangi dampak terhadap operasi write yang konkuren, tetapi masing-masing memiliki karakteristik lock, beban, dan pemulihan kegagalan (failure-recovery) yang berbeda.

Terakhir, pewawancara mencari gate berbasis invariant. Jawaban yang kuat tidak sekadar mencantumkan "tambah kolom, dual-write, backfill, drop kolom." Jawaban tersebut harus menjelaskan kapan setiap writer kompatibel, bagaimana write yang terlewat dideteksi, mengapa backfill bersifat idempoten, kapan membaca hanya dari kolom baru menjadi aman, ke titik mana setiap fase dapat di-rollback, dan kapan kolom lama tidak lagi menjadi jalur pemulihan yang praktis.

Pertanyaan Klarifikasi

  • Di mana saja lokasi semua writer? Buat inventaris layanan web, worker, scheduled job, skrip administratif, fungsi database, trigger, CDC, ETL, query BI, view, dan integrasi eksternal. Satu saja writer yang belum di-upgrade dapat terus-menerus memicu inkonsistensi.
  • Apakah nilai kolom harus tetap sama persis selama migrasi? Dalam masalah ini, ya: ini murni penggantian nama. Jika handle juga memperkenalkan normalisasi, aturan huruf besar/kecil baru, atau nilai yang dipilih pengguna, penanganan konflik akan menjadi masalah migrasi data yang berbeda.
  • Bagaimana keunikan username diimplementasikan? Kolom baru membutuhkan indeks unik atau constraint yang setara. Konfirmasikan bahwa case sensitivity, collation, perilaku null, dan predikat parsial apa pun tidak berubah.
  • Bisakah perubahan aplikasi dan database dirilis secara terpisah? Harus bisa. Jika skema dan aplikasi hanya dapat di-deploy sebagai satu operasi yang tidak dapat dipisahkan, tim tidak dapat melangkah melalui beberapa fase kompatibilitas.
  • Berapa lama migrasi boleh berjalan? Melakukan backfill pada 200 juta baris mungkin memakan waktu berjam-jam atau berhari-hari. Desain memerlukan kebijakan jeda (pause), lanjut (resume), penyelesaian, dan retensi kolom lama yang dapat bertahan di beberapa rilis.
  • Apa arti rollback? Me-rollback kode aplikasi, menghentikan backfill, memulihkan write kolom lama, dan memulihkan kolom yang di-drop adalah empat operasi yang berbeda. Rollback aplikasi normal tidak lagi aman setelah kolom lama dihapus.

Jawaban 30 Detik

"Saya tidak akan mengganti nama secara langsung (in place). Saya akan menggunakan pola expand-and-contract. Pertama, tambahkan handle yang nullable dengan lock_timeout yang singkat; jika lock tidak tersedia, gagalkan dan coba lagi. Rilis kompatibilitas A masih membaca username tetapi menulis ke kedua kolom secara atomik. Setelah seluruh 80 instance dan setiap worker di-upgrade, lakukan backfill pada baris dengan handle IS NULL dalam batch primary-key yang kecil dan idempoten. Pantau null, ketidakcocokan, error write, lock, p99, replication lag, WAL, dan autovacuum secara terus-menerus. Setelah backfill, buat indeks unik baru secara konkuren, tambahkan check NOT VALID, validasi secara terpisah, lalu tetapkan NOT NULL. Rilis B mengutamakan handle dengan fallback ke username dan mempertahankan dual write. Setelah masa observasi, baca hanya dari handle, lalu hentikan write ke kolom lama. Pertahankan kolom lama selama jendela rollback penuh dan drop kolom tersebut hanya setelah kode, job, view, dan consumer tidak lagi mereferensikannya. Kegagalan pada gate akan membiarkan sistem tetap berada pada fase kompatibel saat ini; men-drop kolom tidak diperlakukan sebagai titik rollback biasa."

Solusi Langkah demi Langkah

Tentukan Invariant dan Gate Migrasi Terlebih Dahulu

Sebelum mengeksekusi DDL, tuliskan empat invariant:

  1. Selama fase versi campuran, username tetap menjadi sumber kebenaran (source of truth) untuk rollback. handle boleh bernilai null, tetapi nilai yang non-null harus sama dengan username.
  2. Setelah rilis kompatibilitas menjangkau setiap writer, setiap write baru harus mengupdate kedua kolom dalam satu transaksi. Satu kolom tidak boleh berhasil tanpa kolom lainnya.
  3. Operasi read tidak boleh dilepas dari kolom lama sampai jumlah ketidakcocokan null dan non-null bernilai nol, semua writer kompatibel, serta indeks dan constraint baru valid.
  4. Kontraksi skema tidak boleh dimulai sampai kode produksi, background job, view, laporan, CDC, dan build rollback tidak lagi memerlukan username.

Catat versi deployment, waktu mulai, penanggung jawab, progress cursor, dashboard, dan kondisi keluar (exit conditions) untuk setiap fase. Runner migrasi harus memegang lease atau PostgreSQL advisory lock agar dua eksekutor tidak dapat menjalankan langkah yang sama secara bersamaan. Setiap langkah juga memerlukan versi unik dan catatan keberhasilan yang persisten, memungkinkan restart untuk melanjutkan alih-alih mengulang seluruh migrasi dari awal.

Fase Satu: Perluas Skema Tanpa Mengubah Perilaku (Expand)

Lakukan gladi bersih (rehearse) DDL pada salinan database berukuran produksi dan periksa transaksi yang berjalan lama, antrean lock, serta sisa kapasitas disk. Tambahkan kolom nullable tanpa nilai default:

sql
BEGIN;
SET LOCAL lock_timeout = '2s';
ALTER TABLE users ADD COLUMN handle text;
COMMIT;

Kolom nullable tanpa nilai default tidak menulis ulang 200 juta baris, tetapi ALTER TABLE tetap membutuhkan lock yang kuat. lock_timeout menggagalkan upaya yang tidak dapat memperoleh lock dengan cepat, memungkinkan sistem deployment untuk mencoba lagi nanti dengan jitter. Identifikasi transaksi lama yang tidak normal sebelum eksekusi dan amati siapa yang memblokir siapa selama eksekusi. Menunggu tanpa batas waktu tidak aman karena antrean lock yang kuat dapat menyebabkan permintaan tabel berikutnya menumpuk di belakangnya.

Aplikasi lama mengabaikan handle setelah langkah ini, sehingga rollback aplikasi tetap independen. Jangan gabungkan penambahan ini dengan NOT NULL, nilai default yang volatil, unique constraint, dan update penuh. Hal itu akan menggabungkan perubahan metadata, table scan, pembuatan indeks, dan amplifikasi write data menjadi satu operasi berisiko tinggi.

Fase Dua: Deploy Writer yang Kompatibel Sementara Kolom Lama Tetap Menjadi Sumber Read

Rilis A berperilaku sebagai berikut:

  • Operasi read terus menggunakan hanya username, sehingga perilaku yang terlihat oleh pengguna tidak berubah.
  • Pembuatan dan pembaruan pengguna menulis nilai yang sama ke username dan handle dalam satu transaksi SQL.
  • Kegagalan pada salah satu kolom akan me-rollback seluruh transaksi; dual write bukanlah dua request asinkron yang terpisah.
  • Metrik mencatat upaya dual-write, kegagalan, dan ketidakcocokan tanpa mencatat username yang sebenarnya ke log.

Selama peluncuran rilis A, instance yang belum di-upgrade masih menulis hanya ke username, sehingga baris handle IS NULL yang baru dibuat untuk sementara valid jika bernilai null. Pengecualian tersebut ditutup hanya setelah seluruh 80 instance web, worker, scheduled job, dan consumer independen melaporkan versi yang kompatibel. Jika tim tidak dapat mendata setiap writer—misalnya, program pihak ketiga menulis langsung ke database—trigger database sementara dapat digunakan untuk menyinkronkan kolom. Konsekuensinya adalah perilaku write yang tersembunyi, biaya ekstra, serta replikasi, CDC, dan diagnosis insiden yang lebih kompleks. Perlakukan trigger tersebut sebagai infrastruktur migrasi yang memiliki batas tanggal penghapusan.

Fase Tiga: Lakukan Backfill Secara Idempoten Berdasarkan Rentang Primary-Key

Mulai migrasi di latar belakang hanya setelah setiap writer kompatibel. Gunakan rentang keyset primary-key alih-alih OFFSET, dan jaga agar setiap batch berada dalam transaksi yang singkat:

sql
UPDATE users
SET handle = username
WHERE id > $1
  AND id <= $2
  AND handle IS NULL;

Predikat handle IS NULL membuat batch aman untuk dicoba ulang dan menghindari penimpaan nilai yang sudah ditulis oleh request online. Simpan rentang primary-key terakhir yang selesai diproses secara persisten. Runner dapat berhenti setelah batch mana pun dan melanjutkan dari rentang terkonfirmasi terakhir setelah restart. Mulailah dengan satu worker, lalu sesuaikan ukuran batch dan jeda waktu berdasarkan hasil pengukuran tanpa menjanjikan throughput tetap di awal.

Setelah setiap batch, perhatikan write p99, CPU database, waktu tunggu lock, replication lag, WAL, disk, dead tuples, dan autovacuum. Kurangi ukuran batch atau jeda proses segera setelah metrik apa pun mendekati ambang batas produksinya. Tujuannya adalah penyelesaian yang stabil dalam batas SLO. Transaksi tunggal sebesar 200 juta baris tidak dapat dibenarkan hanya karena skripnya lebih pendek.

Jalankan dua validasi independen pada saat yang sama:

sql
SELECT count(*) FROM users WHERE handle IS NULL;

SELECT count(*)
FROM users
WHERE handle IS DISTINCT FROM username;

Hitungan pertama harus turun secara konsisten hingga nol. Hitungan kedua menggunakan IS DISTINCT FROM untuk menangkap perbedaan null dan nilai non-null yang tidak sama. Hasil apa pun yang bukan nol akan memblokir cutover dan harus diinvestigasi berdasarkan rentang primary-key. Proses backfill tidak boleh secara diam-diam menimpa konflik non-null karena hal itu dapat menyembunyikan writer lama atau logika baru yang salah yang masih aktif.

Fase Empat: Tambahkan Indeks dan Constraint

Karena username bersifat unik, kolom baru membutuhkan indeks unik yang setara. Setelah backfill dan validasi duplikat selesai, jalankan perintah ini di luar blok transaksi:

sql
CREATE UNIQUE INDEX CONCURRENTLY users_handle_key
ON users (handle);

Pembuatan secara concurrent memungkinkan operasi write normal terus berjalan, tetapi proses ini melakukan lebih banyak pekerjaan, memindai tabel dua kali, dan menunggu transaksi terkait selesai. Proses ini tetap mengonsumsi CPU dan I/O. Pembuatan yang gagal dapat meninggalkan indeks INVALID. Pemulihan harus memeriksa katalog sistem, menghapus artefak yang gagal, dan mencoba lagi setelah memperbaiki penyebabnya; kegagalan perintah saja tidak membuktikan bahwa database telah kembali ke keadaan semula.

Tetapkan sifat non-null secara bertahap juga:

sql
ALTER TABLE users
  ADD CONSTRAINT users_handle_not_null
  CHECK (handle IS NOT NULL) NOT VALID;

ALTER TABLE users
  VALIDATE CONSTRAINT users_handle_not_null;

ALTER TABLE users
  ALTER COLUMN handle SET NOT NULL;

ALTER TABLE users
  DROP CONSTRAINT users_handle_not_null;

NOT VALID mulai menegakkan pemeriksaan untuk operasi write berikutnya tanpa langsung memindai baris lama. VALIDATE CONSTRAINT kemudian memeriksa baris yang ada dengan tingkat lock yang lebih rendah. CHECK yang valid membuktikan tidak ada nilai null, memungkinkan SET NOT NULL berikutnya untuk melewati full-table scan yang biasanya dilakukan. Setiap pernyataan DDL tetap memerlukan waktu tunggu lock yang singkat, langkah eksekusi terpisah, dan pemantauan produksi. "Concurrent" dan "no rewrite" bukan berarti "bebas biaya."

Fase Lima: Alihkan Read (Cut Over), Lalu Hentikan Write ke Kolom Lama

Rilis B mengutamakan handle, melakukan fallback ke username jika bernilai null, dan melanjutkan dual write atomik. Gate menyatakan bahwa nilai null seharusnya sudah nol, tetapi fallback mempertahankan kompatibilitas untuk rollback dan mengisolasi data yang tidak terduga. Mulailah dengan canary deployment, perluas secara bertahap, dan bandingkan hasil kolom baru vs kolom lama, error, dan metrik bisnis.

Setelah jendela observasi, rilis C membaca hanya dari handle sambil terus menulis ke kedua kolom. Masalah pada jalur read masih dapat di-rollback ke rilis B atau A karena username tetap mutakhir. Hanya setelah periode observasi yang mencakup background job, endpoint berfrekuensi rendah, dan siklus deployment lengkap, rilis D boleh berhenti menulis ke username.

Menghentikan write ke kolom lama akan mengubah semantik rollback. Rollback berikutnya ke build yang hanya mengetahui username memerlukan pemulihan dual write terlebih dahulu dan melakukan reverse-backfill terhadap nilai yang dibuat selama jeda waktu tersebut. Me-rollback aplikasi secara langsung akan mengekspos data yang usang (stale). Cantumkan persyaratan ini dalam runbook dan deployment gate agar penanganan insiden tidak bergantung pada ingatan seorang engineer.

Fase Enam: Tunda Kontraksi (Contract)

Sebelum penghapusan, gunakan pencarian kode, query log, katalog dependensi, dan inventaris consumer untuk membuktikan bahwa username tidak lagi memiliki pembaca. Hapus indeks lama, constraint, trigger, dan dependensi view terlebih dahulu, lalu drop kolom dalam rilis terpisah:

sql
ALTER TABLE users DROP COLUMN username;

Men-drop kolom melewati batas destruktif. Meskipun PostgreSQL tidak langsung menulis ulang tabel, aplikasi lama, cache skema, view, dan query eksternal dapat langsung mengalami kegagalan. Data yang dihapus juga tidak tersedia untuk rollback aplikasi biasa. Proses drop harus dilakukan setidaknya satu jendela retensi rollback penuh setelah cutover read dan write, terpisah dari rilis kode yang berhenti menggunakan kolom tersebut. Backup adalah disaster recovery, bukan mekanisme rollback deployment dengan latensi rendah.

Contoh Jawaban yang Kuat

"Risiko utamanya adalah kompatibilitas selama 30 menit rolling deployment. Saya akan membagi perubahan menjadi fase ekspansi, write yang kompatibel, backfill dan validasi, cutover read, dan penundaan kontraksi.

Pertama, saya menambahkan handle yang nullable dengan lock_timeout yang singkat; jika lock tidak tersedia, upaya tersebut gagal dan mencoba lagi. Kolom nullable tanpa default tidak menulis ulang tabel, tetapi DDL tetap mengambil lock yang kuat, jadi saya memeriksa transaksi lama dan antrean lock. Rilis A masih membaca username, dan setiap writer mengupdate kedua kolom dalam satu transaksi database. Backfill dimulai hanya setelah seluruh 80 instance, worker, dan consumer telah di-upgrade.

Backfill menggunakan transaksi rentang primary-key yang singkat dengan UPDATE ... WHERE handle IS NULL, menyimpan progres secara persisten, dan aman untuk dicoba ulang. Kecepatannya mengikuti p99 produksi, replication lag, WAL, dead tuples, dan autovacuum. Jumlah null harus mencapai nol, dan handle IS DISTINCT FROM username harus tetap nol. Konflik non-null diinvestigasi alih-alih langsung ditimpa.

Setelah data gate terpenuhi, saya membuat indeks unik yang setara menggunakan CREATE UNIQUE INDEX CONCURRENTLY dan menangani sisa indeks yang tidak valid jika terjadi kegagalan. Saya menambahkan CHECK ... NOT VALID, memvalidasinya secara terpisah, lalu menyetel NOT NULL. Rilis B mengutamakan kolom baru dengan fallback ke kolom lama dan mempertahankan dual write. Rilis C membaca hanya dari kolom baru tetapi tetap melakukan dual-write. Rilis D menghentikan write ke kolom lama hanya setelah satu siklus observasi penuh.

Setiap fase memiliki jalur rollback. Sebelum cutover read, saya dapat me-rollback aplikasi secara langsung. Saat membaca kolom baru tetapi masih melakukan dual-write, saya dapat kembali membaca kolom lama. Setelah write lama dihentikan, saya harus memulihkan dual write dan melakukan reverse-backfill sebelum aplikasi lama aman digunakan. Terakhir, setelah kode, job, view, CDC, dan laporan tidak lagi mereferensikan username dan jendela rollback telah lewat, saya men-drop kolom tersebut dalam rilis terpisah. Kegagalan pada gate akan membiarkan sistem dalam kondisi kompatibel alih-alih memaksakan kontraksi destruktif."

Kesalahan Umum

  • Mengganti nama kolom secara langsung (in place) → Instance lama dan baru memerlukan nama yang berbeda selama rollout → Gunakan kolom baru dan beberapa rilis yang kompatibel.
  • Mengupdate 200 juta baris dalam satu transaksi → Transaksi memperbesar WAL, lock, replication lag, bloat, dan waktu pemulihan → Gunakan batch rentang primary-key yang singkat dan idempoten.
  • Memulai backfill segera setelah dual write dimulai → Instance yang belum di-upgrade masih dapat membuat nilai null baru → Tunggu hingga setiap writer kompatibel sebelum menetapkan nol null sebagai gate.
  • Mengimplementasikan dual write sebagai dua request terpisah → Timeout dapat menyebabkan hanya satu kolom yang terupdate → Update keduanya secara atomik dalam satu transaksi database.
  • Hanya memeriksa handle IS NULL Nilai non-null yang tidak sama luput dari deteksi → Periksa juga IS DISTINCT FROM dan investigasi setiap konflik.
  • Menganggap ADD COLUMN bebas lock → DDL metadata tetap membutuhkan lock tabel, dan permintaan DDL yang mengantre dapat memperparah pemblokiran → Gunakan batas tunggu lock yang singkat, retry, dan pemantauan lock/transaksi.
  • Membuat indeks unik secara normal → Pembuatan indeks pada tabel besar dapat memblokir writer untuk durasi yang tidak dapat diterima → Gunakan CONCURRENTLY serta tangani beban ekstra dan pemulihan indeks yang tidak valid.
  • Men-drop kolom lama segera setelah backfill → Worker berfrekuensi rendah, view, cache skema, atau build rollback mungkin masih membutuhkannya → Tunda kontraksi melewati jendela observasi dan rollback yang lengkap.
  • Memperlakukan backup sebagai tombol rollback → Merestore database 200 juta baris jauh lebih lambat dan berisiko dibandingkan rollback aplikasi → Pertahankan struktur yang kompatibel secara online hingga batas destruktif tercapai.
  • Menjanjikan "migrasi tanpa dampak sama sekali" (zero-impact) → DDL, indeks, dan backfill semuanya mengonsumsi lock atau sumber daya → Janjikan ketiadaan pemadaman terencana, lindungi SLO, dan jeda proses sebelum ambang batas terlampaui.

Pertanyaan Lanjutan

Mengapa tidak menambahkan handle dengan nilai default?

PostgreSQL dapat menghindari penulisan ulang tabel untuk nilai default konstan yang non-volatil, tetapi tidak ada konstanta yang dapat mengekspresikan "salin username yang ada pada baris ini." Bahkan nilai default yang cepat secara fisik tetap membutuhkan lock DDL dan tidak menyelesaikan masalah instance lama yang hanya menulis ke kolom lama. Migrasi ini tetap membutuhkan writer yang kompatibel dan proses backfill data. Apakah operasi insert di masa mendatang memerlukan nilai default adalah keputusan semantik bisnis, bukan pengganti rencana rollout.

Bagaimana jika handle harus diubah menjadi huruf kecil (lowercase) dan dibuat unik kembali?

Itu bukan lagi sekadar penggantian nama murni. Tentukan fungsi normalisasi dan kebijakan konflik, lalu hitung jumlah tabrakan (collision) di lower(username) pada replika atau offline job. Putuskan apakah akan mempertahankan nilai, menambahkan sufiks, atau meminta tindakan pengguna. Simpan nilai asli dan status konversi selama backfill, dan buat indeks unik hanya setelah konflik bernilai nol. Operasi write online dan backfill harus menggunakan implementasi normalisasi yang persis sama.

Bagaimana jika tidak semua writer dapat di-upgrade bersamaan?

Untuk sistem eksternal yang tidak dapat dimigrasikan dengan cepat, tambahkan trigger database sementara yang menyalin write dari kolom lama ke kolom baru dan mencatat penggunaannya agar pemanggil yang tersisa dapat ditemukan. Trigger harus menolak request yang memberikan nilai yang bertentangan. Evaluasi perilaku rekursi, replikasi, dan CDC. Setelah setiap pemanggil dimigrasikan, nonaktifkan dan amati sebelum menghapus trigger agar logika bisnis yang tersembunyi tidak menjadi permanen.

Bagaimana jika replication lag terus meningkat selama backfill?

Jeda batch baru dan biarkan replika mengejar ketertinggalan alih-alih menambah worker untuk mengejar progres. Periksa ukuran batch, ritme commit, laju WAL, query yang berjalan lama, dan autovacuum. Lanjutkan kembali dengan batch yang lebih kecil dan konkurensi yang lebih rendah. Jika operasi read bergantung pada replika, replication lag sudah menjadi risiko yang terlihat oleh pengguna; tanggal penyelesaian backfill berada di bawah prioritas SLO produksi.

Bisakah CREATE UNIQUE INDEX CONCURRENTLY dijalankan ulang begitu saja setelah gagal?

Jangan jalankan ulang secara membabi buta. Indeks yang tidak valid dengan nama yang sama mungkin tertinggal di katalog sistem, dan pembuatan unik secara concurrent mungkin telah menegakkan keunikan terhadap transaksi lain selama fase yang gagal. Periksa validitas indeks, identifikasi kegagalan data atau sumber daya, hapus indeks yang gagal sesuai runbook, lalu buat ulang. Perintah ini juga tidak dapat berjalan di dalam blok transaksi biasa, sehingga alat migrasi harus mendukung mode eksekusi tersebut.

Fase mana yang paling sulit di-rollback?

Setelah write ke kolom lama dihentikan, nilai lama mulai tertinggal. Setelah kolom lama di-drop, baik data maupun skema telah melintasi batas destruktif. Skenario pertama memerlukan pemulihan dual write dan reverse-backfill sebelum build lama aman digunakan. Skenario kedua biasanya memerlukan perbaikan ke depan (forward repair) atau pemulihan backup. Pisahkan tindakan-tindakan ini ke dalam rilis yang berbeda dan jaga agar kolom lama tetap mutakhir selama jendela observasi yang cukup panjang.

Sumber publik

Pertanyaan terkait