Seconds_Behind_Source sudah 0. Angka itu terasa seperti tanda aman, padahal tidak membuktikan replika sudah punya semua transaksi dari server lama. Kalau cutover dilakukan saat replika masih tertinggal beberapa transaksi, data itu tidak rusak. Data itu tertinggal di server lama yang sudah tidak dipakai, dan biasanya baru ketahuan saat ada pesanan atau pembayaran yang tidak tercatat di server baru.
Untuk paham kenapa angka nol bisa menipu, kita perlu melihat jalur sebuah transaksi dari server lama ke replika. Contoh di artikel ini memakai replikasi asynchronous bawaan MySQL. Konsepnya mirip di MariaDB, tapi nama perintahnya bisa berbeda.
Jalur sebuah transaksi dari server lama ke replika
- Replication I/O thread (receiver) menerima event dari source lalu menuliskannya ke relay log, yaitu salinan lokal binlog di server replika.
- Replication SQL thread (applier) membaca relay log dan benar-benar mengeksekusi perubahan itu ke tabel. Kalau replikasi paralel aktif, SQL thread membagi pekerjaannya ke beberapa worker.
Kenapa Seconds_Behind_Source = 0 belum cukup
Seconds_Behind_Source (di versi lama namanya Seconds_Behind_Master) dihitung dari selisih waktu antara jam replika saat ini dan timestamp event yang sedang diterapkan SQL thread. Artinya, angka ini hanya mengukur tahap kedua. SQL thread tidak tahu apa yang masih ada di source tapi belum dikirim.
Akibatnya ada beberapa situasi di mana angkanya terlihat bagus padahal replika belum lengkap:
I/O thread masih berjalan tapi lambat menerima event, misalnya karena bandwidth antar data center terbatas. SQL thread sudah menghabiskan semua isi relay log dan sedang menunggu. Dari sudut pandangnya tidak ada yang tertinggal, jadi angkanya 0.
I/O thread berhenti karena error koneksi atau autentikasi. Dalam kondisi ini nilainya biasanya menjadi NULL, bukan angka besar. Kalau monitoring hanya memeriksa apakah nilainya di atas ambang batas, NULL bisa lolos tanpa alarm.
Source sedang sepi. Kalau tidak ada transaksi baru, lag memang 0. Tapi begitu trafik datang lagi sesaat sebelum cutover, angka itu sudah tidak berlaku.
Jam source dan replika berbeda. MySQL mencoba mengoreksi selisih jam saat replika terhubung, tapi kalau jam bergeser setelah itu, nilai detiknya bisa ikut melenceng.
Kesimpulannya, Seconds_Behind_Source berguna untuk memantau tren selama sinkronisasi awal, tapi bukan bukti untuk keputusan cutover. Untuk itu kita butuh perbandingan posisi yang eksak.
Membaca SHOW REPLICA STATUS dengan benar
SQL
-- Di server lama (source). MySQL 8.4 ke atas: SHOW BINARY LOG STATUS;
SHOW MASTER STATUS;
-- Di server baru (replika). Sebelum MySQL 8.0.22: SHOW SLAVE STATUS\G
SHOW REPLICA STATUS\GTEXT
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Source_Log_File: mysql-bin.000120
Read_Source_Log_Pos: 4500000
Relay_Source_Log_File: mysql-bin.000120
Exec_Source_Log_Pos: 4500000
Seconds_Behind_Source: 0
Last_IO_Error:
Last_SQL_Error:
Replica_SQL_Running_State: Replica has read all relay log; waiting for more updatesReplica_IO_Running dan Replica_SQL_Running harus sama-sama Yes. Kalau salah satunya No atau Connecting, berhenti di sini dan cek Last_IO_Error atau Last_SQL_Error. Isi kedua kolom error itu juga harus kosong.
Source_Log_File dan Read_Source_Log_Pos menunjukkan posisi yang sudah diterima I/O thread. Bandingkan dengan kolom File dan Position dari SHOW MASTER STATUS di source. Kalau berbeda, berarti masih ada event yang belum sampai ke replika.
Relay_Source_Log_File dan Exec_Source_Log_Pos menunjukkan posisi yang sudah diterapkan SQL thread. Posisi ini dihitung dalam koordinat binlog source, jadi bisa langsung dibandingkan dengan output di source. Replika baru dianggap sinkron kalau posisi terima dan posisi terapkan sama-sama sama dengan posisi source.
Perbandingan ini hanya berarti kalau source sedang tidak menerima penulisan. Kalau aplikasi masih menulis, posisi source terus bergerak dan hasil perbandingan sudah usang begitu kamu selesai membacanya.
Dari sisi support, ini juga pola yang sering muncul. Ada yang bilang replikasinya sudah sinkron, tapi buktinya hanya screenshot lag 0 tanpa posisi source. Yang paling berguna untuk diminta adalah output lengkap SHOW REPLICA STATUS beserta posisi source yang diambil pada saat yang sama, setelah penulisan dihentikan.
Membandingkan dengan GTID
uuid_server:nomor_urut. Server menyimpan himpunan semua GTID yang sudah ia jalankan di variabel gtid_executed.
Logikanya sederhana. Setelah penulisan di source dihentikan, ambil nilai gtid_executed dari source. Lalu minta replika menunggu sampai seluruh himpunan itu selesai diterapkan. Fungsi WAIT_FOR_EXECUTED_GTID_SET menangani hal ini dan mengembalikan 0 kalau berhasil atau 1 kalau timeout.
Perhatikan bahwa langkah pertama di bawah mengubah state server produksi, jadi bukan sekadar diagnostik. Jalankan hanya saat maintenance window sudah dimulai dan aplikasi siap berhenti menulis.
SQL
-- 1. Server lama: hentikan semua penulisan (otomatis menyalakan read_only juga)
SET GLOBAL super_read_only = ON;
-- 2. Server lama: catat himpunan GTID yang sudah di-commit
SELECT @@GLOBAL.gtid_executed;
-- 3. Server baru: tunggu sampai himpunan itu selesai diterapkan, timeout 60 detik
SELECT WAIT_FOR_EXECUTED_GTID_SET('tempel_hasil_langkah_2', 60);super_read_only, bukan hanya read_only. read_only biasa masih mengizinkan user dengan privilege tingkat tinggi seperti SUPER untuk menulis. Kalau aplikasi terhubung memakai user root, read_only saja tidak cukup untuk membekukan data.
Kalau hasil langkah 3 adalah 0, berarti setiap transaksi yang pernah di-commit di source sudah ada di replika. Kalau hasilnya 1, jangan diulang terus sambil berharap hasilnya berubah. Cek dulu status thread dan kolom error di SHOW REPLICA STATUS.
Untuk MariaDB, format GTID-nya berbeda (domain-server-urutan), dan fungsi tunggunya juga berbeda. Cek dokumentasi versi MariaDB yang kamu pakai sebelum menyalin perintah di atas.
Urutan cutover yang aman
- Hentikan semua sumber penulisan ke server lama. Ini mencakup aplikasi utama, cron, queue worker, dan proses lain yang menulis ke database.
super_read_onlyadalah pengaman terakhir, bukan pengganti mematikan worker. Kalau worker masih jalan, mereka akan error saat mencoba menulis. - Ambil posisi akhir source, berupa binlog file dan position atau
gtid_executed. - Di replika, pastikan kedua thread berstatus
Yes, kolom error kosong, dan posisi terima serta posisi terapkan sudah sama dengan posisi source. Kalau memakai GTID, gunakanWAIT_FOR_EXECUTED_GTID_SET. - Hentikan replikasi di server baru dengan
STOP REPLICA. Kalau ingin membuang konfigurasi replikasi sepenuhnya, ada perintah reset, tapi efeknya tidak bisa dibatalkan. Sebaiknya lakukan setelah server baru terbukti stabil. - Buat server baru bisa ditulis dengan mematikan
super_read_onlydanread_only, lalu arahkan aplikasi ke server baru. Kalau pemindahan memakai DNS, ingat bahwa sebagian client bisa masih ke IP lama untuk sementara. Pembahasannya ada di Kenapa Website Masih ke Server Lama Padahal TTL Sudah Diturunkan. - Biarkan server lama tetap dalam mode read-only. Jangan buka penulisan di sana lagi. Kalau ada client yang masih nyasar, lebih baik mereka mendapat error yang terlihat daripada menulis data yang diam-diam tidak pernah sampai ke server baru.
Seconds_Behind_Source sebagai indikator kemajuan, bukan bukti sinkron. Bukti yang bisa dipegang adalah penulisan yang sudah dibekukan di source, dua thread replika yang sehat, dan posisi binlog atau GTID yang sama persis di kedua sisi. Kalau ketiganya terpenuhi, cutover tinggal soal memindahkan koneksi. Kalau salah satunya belum, waktu tambahan beberapa menit di maintenance window jauh lebih murah daripada mencari transaksi yang tertinggal di server lama.