Skip to content

Legacy Migration Toolkit

By Romi Fadli, S.Kom. 11 min read Backup & Recovery
On this page
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Legacy Migration Toolkit adalah empat perintah yang memindahkan stack yang sedang berjalan dari layout lamanya ke struktur baru, melintasi versi mayor database dan mesin pencarinya. Satu perintah mengekspor semuanya dari server lama; satu mengimpornya ke struktur baru, tiap store dengan cara yang diizinkan versinya; satu memverifikasi apa yang benar-benar tiba; dan satu mengganti nama folder modul tanpa kehilangan data yang bergantung pada nama itu.

Migrasi seperti ini tak bisa berupa salin folder. PostgreSQL menolak menyala di atas data directory yang ditulis versi mayor lebih lama, upgrade mesin pencari di tempat tak bisa dibatalkan, dan catatan yang dipindahkan mencakup log tanda tangan yang wajib bertahan sepanjang masa retensinya.

Sekilas

Toolsexport-legacy-data, import-legacy-data, verify-migration, rename-modules
BentukScript command-line
BahasaBash, yang mengendalikan Docker, pg_dump dan pg_restore, snapshot API Elasticsearch, rsync, dan openssl
StatusStabil, sudah dipakai untuk migrasi yang selesai
Kode sumberPrivat
Apa toolkit ini, dibangun dengan apa, dan keadaannya.

Yang dikerjakannya

A. Mengekspor tanpa mengubah data sumber

Export men-dump tiap database, menyalin object storage, mengambil snapshot indeks telemetry lewat API milik mesin pencari itu sendiri, dan mengumpulkan runtime state yang tak pernah dipegang version control, misalnya daftar pencabutan milik certificate authority. Ia menulis manifest berisi checksum dan jumlah tabel untuk tiap dump, ukurannya, serta versi image sumber. Ruang disk diperiksa lebih dulu, dan ia memperingatkan bila object storage tak akan muat, lalu hasilnya bisa ditulis ke disk lain atau object storage-nya ditinggalkan. Folder export dikunci hanya untuk pemiliknya. Ia memastikan sumbernya memang tampak seperti folder production lama, dan sengaja mempertahankan nama container layout lama, dengan catatan agar tak disatukan, karena sumber tak ikut berubah hanya karena tujuannya berubah. Database di-dump dalam format custom tanpa owner dan privilege, yang toleran terhadap server yang lebih baru dan role yang berbeda di sana. Snapshot telemetry hanya mencakup indeks telemetry, tanpa global state cluster; bila mesin pencari lama belum punya path repository, export mencetak baris compose dan perintah persis untuk menambahkannya, dan repository-nya diserahkan ke user mesin itu lalu disalin keluar lewat container berumur pendek, karena file-nya milik user itu, bukan milik saya. Daftar pencabutan disalin dengan checksum, wajib terbaca sebagai file DER yang sah, diperiksa masa berlakunya, dan dijelaskan bila keliru berbentuk direktori. File environment dan konfigurasi dibawa sebagai rujukan saja, bukan untuk ditimpakan ke struktur baru, yang kuncinya berbeda; log aplikasi ikut dibawa. Progres salinan hanya ditampilkan di terminal, jadi file log tak dipenuhi carriage return.

B. Mengimpor tiap store dengan cara yang diizinkan versinya

Database di-restore dari dump-nya ke versi mayor yang baru. Mesin pencari me-restore snapshot, sesudah data stream yang akan bertabrakan dengannya dibuang. Object storage disalin sebagai file, karena format on-disk-nya tetap terbaca di versi berikutnya. Store cache dan queue ditinggalkan, sebab bukan sumber kebenaran. Daftar pencabutan dipasang di tempatnya, sesudah direktori yang tertinggal di posisinya dihapus, lalu tanggal berlakunya dicetak. Database di-restore dengan opsi clean, if-exists, no-owner, dan no-privileges, jadi restore aman diulang. Policy bucket dipasang ulang sebagai langkah tersendiri; run ini menyalin lewat container bila folder milik root menolak salinan biasa, dan bisa dibatasi ke database, object storage, atau telemetry untuk migrasi bertahap.

C. Menolak sebelum merusak apa pun

Import memvalidasi setiap checksum sebelum menyentuh satu database pun, jadi dump yang rusak menghentikan run selagi tujuan masih utuh. Bila export mencatat indeks pencarian yang terlalu tua bagi mesin baru, restore telemetry dilewati dan reindex diminta lebih dulu, sementara tahap lainnya tetap berjalan. Sesudah tiap restore, jumlah tabel dibandingkan dengan angka yang dicatat export, dan perbedaannya ditandai untuk diperiksa. Kegagalan di dalam satu tahap — database yang tak kunjung siap dalam semenit, error dari tool restore, restore snapshot yang gagal — dilaporkan dan tahap-tahap selebihnya tetap berjalan; perintah verifikasi yang memberi putusan akhir.

D. Memverifikasi apa yang benar-benar ada

Perintah verifikasi memeriksa tujuan, bukan sekadar percaya bahwa script sudah selesai: data ada di tiap store, file terbaca oleh user yang menjalankan tiap container, folder dimiliki user yang dibutuhkan mesin pencari, dan user telemetry sudah tersedia. Satu pemeriksaan yang gagal tak menghentikan yang lain, dan container yang mati dilaporkan sebagai dilewati, bukan gagal, supaya perintah ini tetap berguna selama migrasi dikerjakan bertahap. Prefix container dibaca dari file environment, bukan ditebak dari nama folder, setelah prefix tebakan yang tak pernah ada mengubah setiap pemeriksaan menjadi "dilewati" dan menyembunyikan kerusakan nyata. Ia memastikan server database minimal versi 18, bukti bahwa upgrade mayor sungguh terjadi, dan bahwa daftar pencabutan adalah file yang sah dan belum kedaluwarsa, bukan file yang hilang atau malah direktori. Keterbacaan file diperiksa untuk dua file konfigurasi milik certificate authority: user yang menjalankan tiap image diterjemahkan ke id angkanya dengan bertanya di dalam image, dibaca dari file compose bila container-nya belum ada, dan diperiksa sebelum apa pun dinyalakan, jadi izin yang salah ketahuan tanpa menunggu tiga puluh percobaan health check. Modul yang lahir sesudah export lama tak punya entri di manifest dan cukup punya tabel. Ia diakhiri dengan hitungan lolos, peringatan, dan gagal, lalu keluar dengan 1 bila ada yang gagal.

E. Mengganti nama modul tanpa kehilangan datanya

Folder modul dulu memakai garis bawah dan kini memakai tanda hubung. Karena path data tiap modul memuat namanya, mengganti nama folder ikut memindahkan tempat Docker mencari datanya, jadi perintah rename memindahkan folder data dalam langkah yang sama, dan menjaga riwayat folder bila version control melacaknya. Cakupannya development, production, dan setiap versi staging. Akses root diperiksa sebelum apa pun berubah, setelah prompt password yang butuh terminal pernah meninggalkan folder project ter-rename sementara data-nya belum. Ukuran folder data dibandingkan sebelum dan sesudah dipindah, dan tujuan yang sudah ada tak pernah ditimpa. Nama project compose tiap modul yang di-rename ditulis ulang, jadi yang lama dan yang baru tak dianggap dua project. Nama project jatuh ke nama folder bila kosong, setelah ketiadaannya pernah membuat rename tetap jalan sementara container development masih hidup. Tabel nama yang di-rename membawa peringatan berbingkai agar tak pernah dikenai cari-ganti global garis bawah, setelah satu sapuan seperti itu membuat kedua kolomnya sama dan rename diam-diam tak mengerjakan apa pun sampai production membutuhkannya. Penulisan ulang kunci yang tak menemukan apa pun tak lagi menghentikan run sesudah modul pertama, dan path dihitung saat dipakai, jadi prefix data yang diatur belakangan tetap dihormati.

Cara saya memakainya

bash
export-legacy-data --src old/production --dry-run
export-legacy-data --src old/production
import-legacy-data --from migration-<ts> --target-dir production --dry-run
import-legacy-data --from migration-<ts> --target-dir production
verify-migration --target-dir production --from migration-<ts>
rename-modules --dry-run
StoreLamaBaruCara pindahnya
PostgreSQL1418dump lalu restore; data directory terikat versi mayornya
Elasticsearch89snapshot lalu restore; upgrade di tempat hanya satu arah
Object storagetak dipatokrilis yang dipatoksalin folder; formatnya tetap terbaca
Redistak dipatokrilis yang dipatoktak dimigrasi; hanya cache dan queue
Kenapa tiap store butuh jalannya sendiri.
StoreContainer-nya harusAlasan
Object storagematimenyalin ke folder storage yang hidup bisa merusak backend-nya
PostgreSQLhiduprestore butuh server yang menerima koneksi
Elasticsearchhidupsnapshot di-restore lewat API-nya
Keadaan yang dibutuhkan tiap store selama import.

Keputusan rancangan

Export dan import terpisah, dan data sumber tak pernah diubah. Data biasanya harus diamankan sebelum struktur baru siap menerimanya, jadi kedua bagian berjalan di hari berbeda dan bertemu lewat satu folder. Export men-dump, menyalin, dan mengambil snapshot, dan satu-satunya yang ia tambahkan di samping data lama adalah repository snapshot yang dibutuhkan mesin pencari untuk menulis snapshot-nya. Manifest-lah yang membuat import bisa memercayai folder itu: checksum diperiksa sebelum database tujuan mana pun di-drop, jumlah tabel dibandingkan sesudah tiap restore, dan versi image sumber tercatat. Folder itu hanya bisa dibaca pemiliknya, dan export memperingatkan sebelum menggandakan puluhan gigabyte object storage di disk yang terlalu kecil.

Tiap store menyeberangi jarak versi dengan caranya sendiri. PostgreSQL 18 tak mau menyala di atas direktori yang ditulis versi 14, jadi database berpindah sebagai dump. Elasticsearch 9 hanya membaca indeks buatan versi 8, dan begitu ia menyentuh folder data, versi 8 tak bisa lagi membacanya, jadi telemetry berpindah sebagai snapshot, yang portabel lintas versi dan menyisakan titik untuk kembali. Indeks buatan versi 7 sudah tertangkap saat export dan menghentikan restore kecuali dipaksa. Object storage cukup dibawa file-nya, dan Redis, yang hanya menyimpan cache dan queue, ditinggalkan.

Ia tak pernah menyalakan atau mematikan stack sendiri. Kebutuhan tiap store bertolak belakang — object storage mati, PostgreSQL dan mesin pencari hidup — dan import memeriksa masing-masing. Bila keadaannya salah, ia berhenti dan mencetak perintah yang akan membetulkannya, serta menunggu hingga satu menit sampai database menerima koneksi sebelum melaporkan database itu dan beralih ke berikutnya. Kendali atas stack production tetap di tangan orang yang menjalankan migrasi. Perintah rename mengikuti aturan yang sama, dan menolak berjalan selama container terkait masih hidup.

Verifikasi mencari kegagalan yang tampak seperti sukses. File konfigurasi yang hanya bisa dibaca pemiliknya berjalan lancar di laptop saya, karena user id saya kebetulan 1000, sama dengan user di dalam image certificate authority; di server dengan id berbeda, authority itu restart tanpa henti, dan dua service yang menunggunya tampil unhealthy, menunjuk ke mana-mana kecuali ke penyebabnya. User telemetry yang hilang lebih senyap lagi: setiap service tetap sehat, dashboard tetap terbuka, tapi setiap log, trace, dan metrik dibuang — dan itu terjadi selama dua hari di production. Karena itu verifikasi memeriksa keterbacaan file terhadap user id tiap container, kepemilikan folder, dan user telemetry, sebelum ada yang menyatakan migrasi selesai. Pemeriksaan terakhir saya kerjakan sendiri: satu dokumen ditandatangani dari awal sampai selesai.

Spesifikasi

MasukanFolder production lama untuk diekspor; folder export dan environment tujuan untuk diimpor
KeluaranFolder export berisi dump, file object storage, snapshot pencarian, runtime state, dan manifest; store yang sudah di-restore di tujuan; laporan verifikasi
KeamananDry run untuk export, import, dan rename; checksum dump database divalidasi sebelum tujuan mana pun di-drop; jumlah tabel dibandingkan; tak pernah menyalakan atau mematikan stack; data ikut berpindah bersama folder-nya saat rename, dengan ukuran dibandingkan dan tanpa menimpa; kegagalan di dalam tahap export dan import dilaporkan, dan verifikasi yang memberi putusan akhir
PrasyaratDocker; struktur tujuan sudah terpasang; ruang disk untuk export, atau disk kedua; akses jaringan untuk menarik image pembantu kecil saat berjalan
Masukan, keluaran, dan jaminan toolkit ini.

Terkait

Cerita tentang memindahkan database dan arsip log melintasi versi mayor.

Toolkit yang mengambil alih begitu data sudah berpindah; repository snapshot-nya termasuk tempat yang diperiksa verifikasi.

Toolkit yang memberi environment tujuan network-nya sebelum apa pun diimpor ke sana.

Setiap script mengikuti bentuk yang saya berikan pada setiap tool kecil, dalam varian Bash-nya.

Toolkit yang membuat user telemetry production — langkah yang hasilnya diperiksa verifikasi di sini.

Kenapa migrasi diperlakukan seperti pemulihan: titik untuk kembali dulu, baru pindah.

Kenapa telemetry yang kehilangan user-nya dibuang tanpa sepatah kata.

Istilah

Versi mayor
Rilis yang boleh mengubah format on-disk, sehingga data tulisan satu versi mayor belum tentu terbaca versi berikutnya.
Dump dan restore
Mengekspor database sebagai file portabel lalu memuatnya ke server lain, dengan versi yang sama atau lebih baru.
Repository snapshot
Lokasi tempat Elasticsearch menulis snapshot-nya, yang bisa di-restore oleh node lain.
Manifest
File yang mendaftar isi satu export, dengan checksum untuk tiap dump database supaya kerusakan terdeteksi sebelum dipakai.
User id
Angka yang dipakai kernel untuk memutuskan siapa boleh membaca file; namanya tak berpengaruh, hanya angkanya.
Data stream
Rangkaian indeks Elasticsearch yang hanya ditambah, di sini dipakai untuk telemetry.

Technologies covered

  • Bash
  • Elasticsearch
  • PostgreSQL
  • Docker
  • MinIO

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

A purple root circle linked to a blue and an orange intermediate circle, which lead to a signed document with a green seal and to a clock.

Aurograf

DevOps

Platform tanda tangan digital Pemerintah Kota Tangerang Selatan: penanda tangan membuktikan identitasnya, menandatangani PDF hingga PAdES B-LTA dengan sertifikat dari otoritas milik platform sendiri, dan siapa pun bisa memverifikasinya — versi 2, yang saya rancang, bangun, dan operasikan.

10 min read Read more →

Related Articles

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →
Four identical grey keys on the left each become a key of its own colour on the right, every new key marked with a green check; below, a purple key stays behind a red bar

Satu perintah yang merotasi kredensial internal satu environment kunci demi kunci: dry run sebagai default, backup lebih dulu, nilai unik untuk tiap kunci, login sungguhan untuk membuktikan tiap kredensial yang bisa dipakai login, dan rollback bagi yang gagal.

8 min read Read more →