06 β Strategi Migrasi: Big-Bang Cutover¶
Dibaca oleh: tim migrasi (DB + backend). Tujuan: memindahkan data 10 tahun dari MariaDB lama ke PostgreSQL baru sekali jalan pada jendela downtime terjadwal, dengan aman & bisa diaudit.
Keputusan pemilik: big-bang cutover. Big-bang itu sah, tetapi untuk data akuntansi 10 tahun ia hanya aman jika dilatih berkali-kali (dry-run) dan direkonsiliasi 100% sebelum hari-H. Dokumen ini menjadikan itu wajib.
Aturan emas: Tidak ada rekonsiliasi 100%, tidak ada cutover. Data akuntansi tidak mengenal "nanti diperbaiki sambil jalan".
1. Gambaran Fase¶
FASE 0 Persiapan & inventaris (paham total dulu)
FASE 1 Bangun skema + logika baru (Dokumen 03 & 05 jadi skrip nyata)
FASE 2 Bangun pipeline ETL (lama β baru, dengan aturan transformasi)
FASE 3 Dry-run berulang + rekonsiliasi (latihan sampai 100% cocok)
FASE 4 Pembekuan & Cutover (Hari-H) (eksekusi + verifikasi + buka sistem baru)
FASE 5 Stabilisasi & rollback-ready (jaring pengaman pasca-cutover)
2. FASE 0 β Persiapan & Inventaris¶
Selesaikan Tindak Lanjut Dokumen 01 Β§7 dulu. Tanpa ini, estimasi & migrasi buta.
Checklist:
- [~] Inventaris Β±150 prosedur: data awal read-only tersedia di
database/etl/output/routines.csv (154 routine); masih perlu klasifikasi
dipakai / tidak / duplikat.
- [~] Inventaris view (Dokumen 01 Β§5): data awal read-only tersedia di
database/etl/output/views.csv (63 view); masih perlu klasifikasi mana
dipakai vs mati. Konfirmasi tabel
sumber (brg/ktk/t/d/s) memang tunggal β bukan konsolidasi tabel kembar.
- [ ] Petakan semua arti kolom cek (angka 7/56/66/...).
- [ ] Daftar laporan aktif (dari 63 view + prosedur periodic*/omset*/neraca*).
- [ ] Tetapkan tanggal pisah buku (cut-off akuntansi) & jendela downtime
(idealnya saat tutup buku bulanan, beban transaksi minimum).
- [ ] Siapkan server PostgreSQL (uji & produksi), backup MariaDB penuh terbukti
bisa di-restore.
Keluaran FASE 0: Kamus Data (Dokumen 02) lengkap + daftar fitur yang harus tetap jalan.
3. FASE 1 β Bangun Skema & Logika Baru¶
- [ ] DDL Dokumen 03 β skrip migrasi bernomor (
V001__skema_sql, dst.). - [ ] Logika Dokumen 05 β fungsi/trigger PL/pgSQL, tiap fungsi
COMMENT ON. - [ ] Bangun di database
erp: schemapusat/logika/logs+ template tenant (Dokumen 04). - [ ] Unit test logika dengan data buatan kecil: 1 PB β cek lapisan FIFO; 1 PJ β cek HPP, stok, jurnal imbang; RJ/RB; PL (pemindahan); OS (opname).
- [x] Sediakan utilitas stok:
logika.bangun_ulang_stok()β idempoten, untuk dipakai di akhir ETL. Util jurnal massal ditunda sampai sub-fungsi jurnal tidak lagi stub, supaya tidak menghapus jurnal hasil ETL.
4. FASE 2 β Pipeline ETL (Extract β Transform β Load)¶
Alat¶
- Ekstraksi/skema:
pgloaderbaik untuk pemindahan kasar MySQLβPG, tetapi data ini butuh transformasi (rename, ganti tipe data, pemetaan ID). Rekomendasi: pgloaderhanya untuk staging tabel (salin apa adanya ke skemalama_raw). View tidak ikut (tidak menyimpan data) β definisinya ditulis ulang terpisah.- Transformasi nyata pakai SQL
INSERT ... SELECTdarilama_rawβ skema baru. Ini bisa di-review, dites, diulang.
MariaDB ββpgloaderβββΊ PG skema `lama_raw` (mentah, apa adanya)
β SQL transformasi (di Git, bernomor)
ββββΊ `pusat.wilayah_*` untuk referensi global
βΌ
PG schema `tenant_<kode>` untuk data tenant (Dokumen 03/04)
Aturan transformasi (wajib didokumentasikan per tabel)¶
| Aturan | Contoh |
|---|---|
| Rename tabel/kolom | brgβbarang, kdtransβjenis_kode (pakai Kamus Dokumen 02) |
tinyint(1) β boolean |
nostok 1/0 β tanpa_stok true/false |
Kode akun decimalβint |
110.01 Γ 100 β 11001 (normalisasi di staging stg_rekening.kode) |
PK d string β identity |
buang idtrans.idbarang; petakan refid lama β ref_detail_id baru via tabel pemetaan |
| ID lama β ID baru | buat tabel map_transaksi(old_id, new_id) & map_detail(...) agar relasi tetap nyambung |
| Migrasi tabel, bukan view | hanya BASE TABLE di-ETL; semua VIEW di-skip lalu definisinya di-port terpisah ke PostgreSQL |
| Referensi global | wilayah_* dimuat sekali ke pusat.wilayah_*, bukan per schema tenant |
| Skip tabel kerja | temp_*/*_draft/*_temp tidak dimigrasi sebagai histori |
| Bersihkan data kotor | baris yatim (FK menggantung), tanggal invalid β laporkan & putuskan (perbaiki / kecualikan) |
Buang temp_*/*_draft/*lite |
tidak dimigrasi (data kerja, bukan histori) |
Pemetaan ID adalah bagian paling rawan. Karena PK lama dibuat manual dan PK baru
IDENTITY, simpan tabelmap_*(oldβnew) dan terjemahkan semua FK lewat tabel itu. Jangan memaksakan ID lama ke IDENTITY.
Urutan muat (hormati dependensi FK)¶
0. referensi pusat : wilayah_provinsi/kabupaten/kecamatan/desa
1. master tanpa dependensi : akun, klien, lokasi, divisi, satuan,
barang_golongan/kategori/jenis/merk
2. master berdependensi : barang, kontak, jenis_transaksi, termin, pengguna
3. transaksi (header) : transaksi β isi map_transaksi
4. transaksi_detail : pakai map_transaksi β isi map_detail
5. turunan : fifo_lapisan, fifo_konsumsi, jurnal, kas
6. cache : stok (lebih baik: bangun ulang via fungsi, lihat Β§5)
Strategi logika saat muat (penting)¶
Karena trigger-first, memuat 850rb detail dengan trigger aktif = sangat lambat & bisa salah urut FIFO. Pendekatan:
- Nonaktifkan trigger saat memuat data historis (
ALTER TABLE ... DISABLE TRIGGER USER). - Muat data apa adanya (termasuk
hpp,mutasi_stokhasil hitungan lama β histori dipercaya apa adanya, jangan dihitung ulang dari nol untuk masa lalu). - Bangun ulang turunan secara terkontrol di akhir:
logika.bangun_ulang_stok()β isistokdariSUM(mutasi_stok).- Rekonstruksi
fifo_lapisan/fifo_konsumsidari histori (urut tanggal) atau migrasi tabelfifolama apa adanya lalu validasi (lihat keputusan Β§6). - Aktifkan kembali trigger β mulai hari-H semua transaksi BARU lewat logika baru.
Keputusan yang harus diambil pemilik+DBE di FASE 0: untuk histori, migrasi
fifolama apa adanya (lebih aman, angka HPP historis tak berubah) vs rekonstruksi FIFO (lebih bersih tapi bisa menggeser HPP lama). Default rekomendasi: migrasi apa adanya untuk masa lalu; logika FIFO baru hanya berlaku untuk transaksi sejak cutover.
5. FASE 3 β Dry-Run Berulang + Rekonsiliasi¶
Jalankan seluruh pipeline di server uji, berulang sampai lolos 100%. Setiap dry-run dicatat (tanggal, durasi, temuan, perbaikan).
Wajib cocok (rekonsiliasi) β lama vs baru¶
| Pemeriksaan | Cara |
|---|---|
| Jumlah baris per entitas | COUNT(*) transaksi, detail, jurnal, barang, kontak β sama (kecuali yang sengaja dibuang, terdokumentasi) |
| Saldo per akun | SUM(debitβkredit) per akun = sama |
| Total piutang & hutang | per kontak = sama |
| Stok akhir per barang/lokasi | = sama (toleransi 0) |
| Total HPP per periode | = sama (atau selisih terjelaskan oleh keputusan FIFO Β§4) |
| Neraca & Rugi/Laba per periode | nilai akhir = sama |
| Jurnal imbang | setiap transaksi: SUM(debit)=SUM(kredit) (di baru harus 0 pelanggaran) |
| Spot-check dokumen | ambil 30 nota acak (PJ/PB/RJ/PL/OS) β bandingkan baris per baris |
Sediakan skrip rekonsiliasi otomatis yang mengeluarkan satu laporan "LULUS/GAGAL" + daftar selisih. Cutover hanya jika LULUS total.
Ukur durasi¶
Catat berapa lama pipeline berjalan di volume nyata (β196rb/852rb/590rb baris). Durasi ini menentukan panjang jendela downtime hari-H. Tambah margin 2Γ.
6. FASE 4 β Hari-H (Cutover)¶
Runbook (jam-J). Setiap langkah punya penanggung jawab & checkbox:
T-1 hari : pengumuman downtime ke semua user; freeze fitur baru; backup penuh terjadwal
T0 : 1. Tutup akses semua aplikasi (Delphi/Flutter/Web) ke sistem lama
2. Backup final MariaDB (verifikasi backup bisa dibaca)
3. Ekstrak data final β jalankan pipeline ETL ke PostgreSQL
4. Jalankan bangun_ulang_stok() + verifikasi jurnal imbang
5. Jalankan SKRIP REKONSILIASI otomatis
6. GERBANG KEPUTUSAN:
- LULUS 100% β lanjut langkah 7
- GAGAL β JALANKAN ROLLBACK (Β§7), batalkan cutover
7. Arahkan backend/aplikasi ke PostgreSQL (ganti koneksi/env)
8. Uji asap di produksi: login β pilih tenant β buat 1 transaksi
uji tiap jenis kritis (PJ/PB/RJ/PL/OS) β cek stok, HPP, jurnal β hapus
9. Buka akses user (mulai bertahap: tim internal dulu, lalu semua)
T+0..72j : pemantauan ketat (lihat Β§8)
Aturan: langkah 6 adalah gerbang tak bisa ditawar. Pewenang go/no-go ditetapkan di muka (pemilik atau yang ditunjuk).
7. Rencana Rollback (wajib ada sebelum hari-H)¶
Big-bang tanpa rollback = judi. Karena selama cutover sistem lama hanya dibekukan (read-only), tidak dihapus, rollback = kembali memakainya.
Pemicu rollback : rekonsiliasi GAGAL, atau cacat fatal ditemukan < T+X jam
Langkah:
1. Tutup akses ke sistem baru
2. Buka kembali akses aplikasi ke MariaDB lama (yang masih utuh & beku)
3. Karena belum ada transaksi baru di sistem lama selama beku β tak ada data
yang perlu direkonsiliasi balik
4. Umumkan ke user; jadwalkan dry-run/cutover ulang setelah akar masalah dibereskan
Syarat agar rollback aman: selama jendela cutover, TIDAK ADA transaksi nyata di sistem baru sebelum gerbang LULUS dilewati. Itu sebabnya uji asap (langkah 8) memakai transaksi dummy yang langsung dihapus.
Batas waktu rollback: tetapkan "titik tak bisa balik" (mis. T+24 jam setelah user mulai transaksi nyata di sistem baru). Lewat itu, perbaikan = maju (fix-forward), bukan mundur. Komunikasikan ini ke pemilik di muka.
8. FASE 5 β Stabilisasi¶
- Pantau
log_error, performa query, dan jurnal tidak imbang (harus 0) 72 jam. - Sediakan view tenant
v_jurnal_tidak_imbang&v_stok_negatifsebagai alarm harian. - Catat semua keluhan user β triase: bug migrasi vs bug fitur vs salah paham.
- Backup PostgreSQL harian + uji restore mingguan sejak hari-1.
- Setelah 2β4 minggu stabil & rekonsiliasi periodik aman β arsipkan MariaDB lama (jangan dihapus; simpan sebagai arsip read-only minimal 1 tahun untuk audit).
9. Risiko Spesifik Big-Bang & Mitigasi¶
| Risiko | Mitigasi |
|---|---|
| Jendela downtime tak cukup (ETL kelamaan) | Ukur durasi di FASE 3, margin 2Γ, optimalkan muat (COPY, index dibuat setelah muat) |
| Rekonsiliasi gagal di hari-H | Sudah dilatih berkali-kali; gerbang go/no-go + rollback siap |
| Fitur tersembunyi hilang (prosedur tak ter-port) | Lampiran Dokumen 05 Β§9 wajib penuh sebelum FASE 4 |
| Data 10 tahun kotor | Dibersihkan & didokumentasikan di FASE 2; keputusan baris bermasalah dicatat |
| Pemetaan ID/relasi putus | Tabel map_* + uji integritas FK 0 yatim sebelum LULUS |
| HPP historis bergeser | Keputusan "migrasi FIFO apa adanya untuk masa lalu" (Β§4) |
Lampiran A β Perintah Bantu Inventaris (READ-ONLY, aman)¶
Jalankan di koneksi read-only ke MariaDB lama. Untuk memahami, bukan mengubah.
Daftar trigger:
SELECT EVENT_OBJECT_TABLE, ACTION_TIMING, EVENT_MANIPULATION, TRIGGER_NAME
FROM information_schema.TRIGGERS
WHERE TRIGGER_SCHEMA='u1566482_sparepart'
ORDER BY EVENT_OBJECT_TABLE, ACTION_TIMING;
Daftar prosedur/fungsi + ukuran (untuk prioritas port):
SELECT ROUTINE_TYPE, ROUTINE_NAME, CHAR_LENGTH(ROUTINE_DEFINITION) AS panjang
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA='u1566482_sparepart'
ORDER BY panjang DESC;
Pisahkan TABEL vs VIEW (klarifikasi: yang di-ETL hanya BASE TABLE):
SELECT TABLE_TYPE, COUNT(*) FROM information_schema.TABLES
WHERE TABLE_SCHEMA='u1566482_sparepart' GROUP BY TABLE_TYPE;
-- Hasil acuan: BASE TABLE = 120, VIEW = 63
Tabel dasar terbesar (prioritas ETL & uji performa):
SELECT TABLE_NAME, TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA='u1566482_sparepart' AND TABLE_TYPE='BASE TABLE'
ORDER BY TABLE_ROWS DESC LIMIT 30;
Daftar view + definisinya (untuk port ke PostgreSQL, bukan ETL):
SELECT TABLE_NAME FROM information_schema.VIEWS
WHERE TABLE_SCHEMA='u1566482_sparepart' ORDER BY TABLE_NAME;
-- lalu: SHOW CREATE VIEW <nama>; β tulis ulang ke sintaks PostgreSQL bila masih dipakai
Cek jurnal tidak imbang di sistem LAMA (baseline kualitas data):
SELECT idtrans, SUM(debit) d, SUM(kredit) k
FROM j GROUP BY idtrans HAVING ROUND(SUM(debit),2) <> ROUND(SUM(kredit),2);
Hasil baseline ini penting: jika sistem lama sendiri sudah punya jurnal tak imbang, putuskan di FASE 0 apakah diperbaiki dulu atau dimigrasi apa adanya dengan catatan. Jangan sampai sistem baru "disalahkan" atas data lama yang sudah bermasalah.