Belajar Flyway Episode 2: Cara Kerja Flyway dan Versioned Migrations

Halo! Selamat datang di Episode 2 dari seri belajar Flyway. Di episode sebelumnya kita sudah berkenalan dengan Flyway secara umum, yang teman-teman bisa baca ulang disini, ya (Belajar Flyway Episode 1: Pengenalan dan Migrasi Pertama). Nah, pada episode kali ini kita akan membahas lebih dalam tentang cara kerja Flyway, terutama mengenai versioned migrations (migrasi berversi). Kita juga akan melihat bagaimana Flyway mengeksekusi file migrasi berprefix V__, bagaimana Flyway menyimpan histori migrasi di database, serta sekilas perbedaan antara versioned vs. repeatable migrations. Gaya pembahasannya akan santai dan seperti ngobrol langsung, supaya mudah diikuti. Yuk, kita mulai! Cara Kerja Flyway Secara Umum Flyway adalah sebuah tool open-source untuk mengelola migrasi database dengan konsep versioning. Cara kerjanya cukup straight-forward: Flyway akan memindai (scan) lokasi file migrasi yang sudah kita tentukan (misalnya folder classpath:db/migration di proyek Java kita) dan membandingkan dengan catatan migrasi yang sudah diterapkan di database. Jika ditemukan perbedaan (ada migrasi baru yang belum pernah dijalankan), Flyway akan menjalankan skrip migrasi tersebut untuk “menutup gap” perbedaan versi antara struktur database dan kode aplikasi. Proses menjalankan perintah migrasi (flyway migrate) ini bersifat idempotent, artinya aman dijalankan berulang kali; jika tidak ada migrasi baru, Flyway tidak akan mengubah apa-apa. Sebagai contoh, misalkan saat ini database kita ada di versi 5 (artinya migrasi terakhir yang terpasang bernomor 5), dan di folder migrasi tersedia file hingga V9. Ketika Flyway dijalankan, ia akan menerapkan migrasi versi 6, 7, 8, dan 9 secara berurutan ke database. Sebaliknya, jika database sudah up-to-date (sudah di versi 9 dan tidak ada file migrasi baru), Flyway tidak akan melakukan apa-apa. Dengan mekanisme ini, Flyway memastikan skema database selalu sync dengan ekspektasi aplikasi di manapun kita menjalankannya (development, staging, produksi, dll). Tip: Biasanya Flyway dijalankan otomatis saat startup aplikasi (misalnya di aplikasi Spring Boot dengan PostgreSQL, migrasi akan berjalan ketika aplikasi dijalankan). Hal ini memastikan setiap instance aplikasi berjalan dengan skema database yang sesuai dengan versi kodenya, jadi tidak ada error karena perbedaan struktur. Tentu, Flyway juga bisa dijalankan manual via command line atau plugin build (Maven/Gradle) tergantung kebutuhan. Konsep Versioned Migration (Migrasi Berversi) Salah satu konsep inti Flyway adalah versioned migrations. Ini adalah skrip migrasi yang memiliki nomor versi unik di namanya, dieksekusi sekali saja dan secara berurutan sesuai urutan versinya. Begitu migrasi dengan versi tertentu dijalankan dan dicatat, Flyway tidak akan menjalankannya lagi di masa mendatang (kecuali kita mengubah versinya atau menghapus catatan historinya secara manual). Penamaan file migrasi berversi biasanya mengikuti format: V__.sql Contoh nama file: V1__Initial_setup.sql, V2__Add_users_table.sql, V2.1__Update_user_role.sql dll. Prefix V menandakan versioned migration. Setelah itu diikuti nomor versi (boleh berupa integer berurutan atau format penomoran berjenjang dengan titik). Lalu dua garis bawah __ sebagai pemisah, diikuti deskripsi singkat mengenai migrasinya. Flyway akan mengurutkan file-file migrasi tersebut berdasarkan nomornya secara numerik dan menjalankannya sesuai urutan tersebut. Penting bahwa setiap file migrasi memiliki versi yang unik; Flyway akan mendeteksi jika ada duplikat versi dan akan menolak menjalankan migrasi yang konflik. Versi bisa berbentuk angka sederhana (1, 2, 3, ...) atau format bertitik (1.1, 1.2, 2.0, dst) sesuai kebutuhan, asalkan urut secara logis. Deskripsi setelah __ tidak berpengaruh ke proses (hanya untuk membantu kita mengingat tujuan migrasi), sedangkan ekstensi file biasanya .sql untuk migrasi SQL (Flyway juga mendukung migrasi berbasis Java, tapi umumnya kita pakai SQL saja). Setelah skrip versioned migration diterapkan di satu lingkungan (terutama lingkungan production atau staging yang sifatnya permanen), best practice-nya adalah tidak mengubah isi skrip tersebut lagi. Kenapa? Karena Flyway menyimpan checksum (hash) dari file migrasi yang sudah dijalankan. Jika kita mengedit file yang sudah terekap di history, Flyway akan mendeteksi checksum-nya tidak cocok saat berikutnya dijalankan dan menandainya sebagai konflik. Solusinya, apabila perlu perubahan skema lagi, cukup buat file migrasi baru dengan nomor versi berikutnya untuk melakukan perubahan tersebut (strategi roll-forward daripada mengubah yang sudah ada). Dengan demikian, riwayat migrasi tetap konsisten dan mudah di-trace kapan perubahan tertentu diperkenalkan. Versioned migration umumnya dipakai untuk perubahan yang incremental, misalnya: membuat/ mengubah/ menghapus tabel, menambah kolom baru, membuat index, mengubah data referensi, atau koreksi data spesifik. Intinya, semua perubahan yang ingin k

Apr 19, 2025 - 10:42
 0
Belajar Flyway Episode 2: Cara Kerja Flyway dan Versioned Migrations

Halo! Selamat datang di Episode 2 dari seri belajar Flyway. Di episode sebelumnya kita sudah berkenalan dengan Flyway secara umum, yang teman-teman bisa baca ulang disini, ya (Belajar Flyway Episode 1: Pengenalan dan Migrasi Pertama). Nah, pada episode kali ini kita akan membahas lebih dalam tentang cara kerja Flyway, terutama mengenai versioned migrations (migrasi berversi). Kita juga akan melihat bagaimana Flyway mengeksekusi file migrasi berprefix V__, bagaimana Flyway menyimpan histori migrasi di database, serta sekilas perbedaan antara versioned vs. repeatable migrations. Gaya pembahasannya akan santai dan seperti ngobrol langsung, supaya mudah diikuti. Yuk, kita mulai!

Cara Kerja Flyway Secara Umum

Flyway adalah sebuah tool open-source untuk mengelola migrasi database dengan konsep versioning. Cara kerjanya cukup straight-forward: Flyway akan memindai (scan) lokasi file migrasi yang sudah kita tentukan (misalnya folder classpath:db/migration di proyek Java kita) dan membandingkan dengan catatan migrasi yang sudah diterapkan di database. Jika ditemukan perbedaan (ada migrasi baru yang belum pernah dijalankan), Flyway akan menjalankan skrip migrasi tersebut untuk “menutup gap” perbedaan versi antara struktur database dan kode aplikasi. Proses menjalankan perintah migrasi (flyway migrate) ini bersifat idempotent, artinya aman dijalankan berulang kali; jika tidak ada migrasi baru, Flyway tidak akan mengubah apa-apa.

Sebagai contoh, misalkan saat ini database kita ada di versi 5 (artinya migrasi terakhir yang terpasang bernomor 5), dan di folder migrasi tersedia file hingga V9. Ketika Flyway dijalankan, ia akan menerapkan migrasi versi 6, 7, 8, dan 9 secara berurutan ke database. Sebaliknya, jika database sudah up-to-date (sudah di versi 9 dan tidak ada file migrasi baru), Flyway tidak akan melakukan apa-apa. Dengan mekanisme ini, Flyway memastikan skema database selalu sync dengan ekspektasi aplikasi di manapun kita menjalankannya (development, staging, produksi, dll).

Tip: Biasanya Flyway dijalankan otomatis saat startup aplikasi (misalnya di aplikasi Spring Boot dengan PostgreSQL, migrasi akan berjalan ketika aplikasi dijalankan). Hal ini memastikan setiap instance aplikasi berjalan dengan skema database yang sesuai dengan versi kodenya, jadi tidak ada error karena perbedaan struktur. Tentu, Flyway juga bisa dijalankan manual via command line atau plugin build (Maven/Gradle) tergantung kebutuhan.

Konsep Versioned Migration (Migrasi Berversi)

Salah satu konsep inti Flyway adalah versioned migrations. Ini adalah skrip migrasi yang memiliki nomor versi unik di namanya, dieksekusi sekali saja dan secara berurutan sesuai urutan versinya. Begitu migrasi dengan versi tertentu dijalankan dan dicatat, Flyway tidak akan menjalankannya lagi di masa mendatang (kecuali kita mengubah versinya atau menghapus catatan historinya secara manual).

Penamaan file migrasi berversi biasanya mengikuti format:

V__.sql

Contoh nama file: V1__Initial_setup.sql, V2__Add_users_table.sql, V2.1__Update_user_role.sql dll.

  • Prefix V menandakan versioned migration.
  • Setelah itu diikuti nomor versi (boleh berupa integer berurutan atau format penomoran berjenjang dengan titik).
  • Lalu dua garis bawah __ sebagai pemisah, diikuti deskripsi singkat mengenai migrasinya.

Flyway akan mengurutkan file-file migrasi tersebut berdasarkan nomornya secara numerik dan menjalankannya sesuai urutan tersebut. Penting bahwa setiap file migrasi memiliki versi yang unik; Flyway akan mendeteksi jika ada duplikat versi dan akan menolak menjalankan migrasi yang konflik. Versi bisa berbentuk angka sederhana (1, 2, 3, ...) atau format bertitik (1.1, 1.2, 2.0, dst) sesuai kebutuhan, asalkan urut secara logis. Deskripsi setelah __ tidak berpengaruh ke proses (hanya untuk membantu kita mengingat tujuan migrasi), sedangkan ekstensi file biasanya .sql untuk migrasi SQL (Flyway juga mendukung migrasi berbasis Java, tapi umumnya kita pakai SQL saja).

Setelah skrip versioned migration diterapkan di satu lingkungan (terutama lingkungan production atau staging yang sifatnya permanen), best practice-nya adalah tidak mengubah isi skrip tersebut lagi. Kenapa? Karena Flyway menyimpan checksum (hash) dari file migrasi yang sudah dijalankan. Jika kita mengedit file yang sudah terekap di history, Flyway akan mendeteksi checksum-nya tidak cocok saat berikutnya dijalankan dan menandainya sebagai konflik. Solusinya, apabila perlu perubahan skema lagi, cukup buat file migrasi baru dengan nomor versi berikutnya untuk melakukan perubahan tersebut (strategi roll-forward daripada mengubah yang sudah ada). Dengan demikian, riwayat migrasi tetap konsisten dan mudah di-trace kapan perubahan tertentu diperkenalkan.

Versioned migration umumnya dipakai untuk perubahan yang incremental, misalnya: membuat/ mengubah/ menghapus tabel, menambah kolom baru, membuat index, mengubah data referensi, atau koreksi data spesifik. Intinya, semua perubahan yang ingin kita terapkan sekali saja dan versi selanjutnya akan bertumpu di atas perubahan tersebut, sebaiknya diformat sebagai versioned migration.

Alur Eksekusi File Versi (V__*) oleh Flyway

Sekarang kita bahas bagaimana Flyway mengeksekusi file-file migrasi berprefix V tersebut. Berikut adalah alur umum setiap kali kita menjalankan perintah migrasi Flyway (misal dengan menjalankan Flyway.migrate() di kode Java atau perintah flyway migrate di CLI):

  1. Scanning/Pencarian Skrip: Flyway akan mencari semua file migrasi di lokasi yang sudah dikonfigurasi. Secara default, Flyway mencari di folder filesystem:src/main/resources/db/migration (jika menggunakan integrasi Spring Boot, cukup letakkan file di resources/db/migration). Flyway memuat semua file yang nama depannya sesuai konvensi (misal dia akan pilih semua file yang diawali V untuk versioned dan R untuk repeatable sesuai aturan nama).
  2. Membandingkan dengan History: Setelah daftar file migrasi didapat, Flyway akan membaca tabel histori migrasi di database (nanti kita bahas detail tabel ini). Dari situ, Flyway tahu versi terakhir yang sudah diterapkan di database serta daftar semua migrasi (beserta checksum-nya) yang pernah dijalankan. File migrasi yang belum ada catatannya di history berarti dianggap pending (belum pernah dijalankan). Sebagai contoh, jika di history terakhir adalah versi 5, maka file V6__...sql ke atas akan dianggap pending. Flyway juga akan mendeteksi kalau ada file baru dengan versi lebih rendah dari versi terakhir (misal mendadak ada V4__...sql padahal DB sudah versi 5) – kasus seperti ini umumnya dianggap error karena versinya out-of-order.
  3. Menjalankan Migrasi Pending: Flyway kemudian mengeksekusi semua versioned migration yang pending tersebut secara berurutan menurut urutan versinya. Eksekusi ini biasanya dilakukan dalam transaksi. Misalnya, pertama dijalankan skrip V6__...sql, selesai sukses, lanjut V7__...sql, dan seterusnya. Jika ada satu skrip gagal di tengah jalan (contoh: perintah SQL di dalamnya error), Flyway akan menghentikan proses migrasi dan (jika database mendukung) melakukan rollback transaksi migrasi yang gagal tersebut, sehingga database tidak setengah-setengah ke-update. Kita harus memperbaiki error tersebut dahulu sebelum menjalankan migrate lagi.
  4. Mencatat Hasil ke Tabel History: Setiap kali satu file migrasi berhasil dijalankan, Flyway akan menyisipkan sebuah record ke tabel histori migrasi di database. Record ini mencatat versi berapa yang dijalankan, nama file apa, checksum file, waktu eksekusi, siapa yang menjalankan, durasi, dan status sukses. Dengan pencatatan ini, Flyway mengingat bahwa versi tersebut sudah ter-install. Jika nanti kita menjalankan migrasi lagi, versi ini tidak akan dieksekusi ulang (kecuali dihapus dari tabel history secara manual atau dilakukan repair khusus).
  5. Repeatable Migrations (Opsional): Setelah semua migrasi versi (V) yang pending selesai, Flyway akan melanjutkan menjalankan repeatable migrations (file R__...sql) jika ada. Repeatable migration yang dijalankan hanyalah yang belum pernah dicatat di history atau yang isi file-nya berubah (checksum-nya berbeda dari catatan sebelumnya). Semua repeatable migration dijalankan paling akhir dalam satu batch migrasi, urut berdasarkan nama/deskripsi file (secara alfabetis). (Kita akan bahas repeatable migration sebentar lagi di bagian berikutnya.)
  6. Selesai: Pada titik ini, Flyway telah menerapkan semua perubahan yang diperlukan. Database kini berada di versi terbaru sesuai skrip-skrip migrasi yang ada. Jika kita tambahkan skrip migrasi baru di kemudian hari, proses di atas akan berulang: Flyway akan mendeteksi ada versi baru yang pending, lalu menjalankannya.

Langkah-langkah di atas terjadi di balik layar setiap kali kita melakukan migrasi. Sebagai pengguna, kita biasanya cukup menulis skrip SQL migrasi sesuai aturan penamaan, menaruhnya di folder yang tepat, lalu memicu Flyway (baik dengan menjalankan aplikasi Java, melalui Maven/Gradle plugin, atau perintah CLI). Flyway yang akan mengurus sisanya secara otomatis.

Flyway Schema History: Menyimpan Riwayat Migrasi di Database

Untuk melacak migrasi apa saja yang sudah dijalankan, Flyway menggunakan sebuah tabel khusus di database. Secara default nama tabelnya adalah flyway_schema_history (dulunya pada Flyway versi lama disebut schema_version). Tabel ini akan otomatis dibuat di schema database yang kita migrasi ketika Flyway pertama kali menjalankan migrasi ke database tersebut.

Tabel *flyway_schema_history* menyimpan histori dari setiap migrasi yang pernah dijalankan.
Gambar di atas menunjukkan contoh isi tabel history setelah menjalankan dua migrasi versioned (versi 1 dan 2). Terlihat ada beberapa kolom penting: installed_rank (nomor urut instalasi migrasi), version (versi migrasi yang dijalankan), description (deskripsi migrasi sesuai nama file), type (tipe migrasi, misal SQL), script (nama file skrip migrasi yang dijalankan), checksum (angka hash untuk mendeteksi perubahan file), installed_by (user yang menjalankan migrasi, di sini user DB postgres), installed_on (timestamp kapan migrasi dijalankan), execution_time (waktu eksekusi dalam milidetik), dan success (status berhasil atau tidaknya migrasi tersebut).

Setiap kali Flyway menjalankan migrasi baru, ia akan menambahkan baris baru ke tabel ini. Dengan adanya flyway_schema_history, Flyway dapat mengetahui versi terakhir dari database serta memastikan skrip yang sama tidak dijalankan dua kali. Misalnya, jika kita sudah punya entri untuk version = 2 di tabel history, lalu kita jalankan Flyway lagi, ia tahu versi 2 sudah ada dan tidak perlu menjalankan ulang V2__...sql. Selain itu, Flyway juga menyimpan checksum dari file pada saat migrasi dijalankan. Jika file migrasi diubah setelah penerapan, checksum-nya akan berbeda dengan catatan di history dan Flyway akan menganggap itu masalah (terdeteksi sebagai perubahan tak diinginkan). Hal ini berguna untuk mencegah seseorang mengedit migrasi yang sudah berjalan di production secara diam-diam – integritas migrasi terjaga.

Catatan: Jika kita terpaksa mengubah skrip migrasi yang sudah telanjur berjalan (misal ada koreksi minor di lingkungan development), Flyway menyediakan perintah repair untuk menyesuaikan kembali catatan di tabel history (misalnya memperbarui checksum). Namun, hal ini sebaiknya dihindari di environment production; lebih baik buat migrasi baru untuk perubahan apapun daripada mengedit sejarah.

Perbedaan Versioned vs. Repeatable Migrations

Terakhir, mari bahas singkat perbedaan antara versioned dan repeatable migrations di Flyway. Sejauh ini kita fokus di migrasi berversi (V__). Flyway juga mendukung migrasi repeatable (R__). Berikut perbedaan utamanya:

  • Versioned Migration (V): Memiliki nomor versi unik di nama filenya. Migrasi ini dijalankan sekali saja per database. Setelah dijalankan, dianggap final dan tidak akan dijalankan lagi di run berikutnya (Flyway tahu sudah done lewat tabel history). Jika ada perubahan skema baru, kita membuat file migrasi versioned baru dengan versi berikutnya. Versioned migration digunakan untuk perubahan yang sifatnya increment (naik versi terus-menerus).
  • Repeatable Migration (R): Tidak memiliki nomor versi, hanya prefix R dan deskripsi. Migrasi ini bisa dijalankan berulang kali apabila ada perubahan isinya. Flyway menentukan perlu tidaknya menjalankan repeatable migration dengan cara memeriksa checksum: setiap kali perintah migrate dijalankan, Flyway akan menjalankan ulang file R__...sql jika dan hanya jika checksum file tersebut berubah dibanding catatan terakhir di history, atau jika file tersebut belum pernah dijalankan sebelumnya. Repeatable migration selalu dijalankan setelah semua versioned migration selesai dalam satu batch migrasi, dan jika ada beberapa repeatable, urutannya berdasarkan alfabet (berdasarkan nama/deskripsi file). Jenis migrasi ini berguna untuk hal-hal yang mungkin perlu diperbarui secara teratur atau di-recreate, misalnya definisi view, stored procedure, function, atau seeding ulang data referensi. Kita bisa menulis script repeatable misalnya R__Refresh_all_views.sql yang berisi perintah CREATE OR REPLACE VIEW ... sehingga aman dijalankan berulang kali. Setiap kali file ini berubah (misal view diubah), Flyway akan re-run script ini di semua environment yang menjalankan migrate.

Singkatnya, versioned migration itu seperti langkah versi yang linier dan hanya sekali jalan, sedangkan repeatable migration itu seperti skrip yang bisa dijalankan berulang jika diperlukan. Keduanya dicatat di tabel history (repeatable punya entri version kosong tapi ada checksum). Dalam praktiknya, sebagian besar migrasi schema akan kita tulis sebagai versioned migration. Repeatable migration dipakai saat diperlukan saja.

(Contoh situasi penggunaan repeatable: Kita punya beberapa SQL untuk membuat laporan (view) yang kompleks. Daripada membuat version baru tiap kali view diubah, kita bisa simpan sebagai R__ script. Setiap ada perubahan, cukup ubah file itu, lalu Flyway akan mendeteksi perubahan dan menerapkannya.)

Demikianlah pembahasan kita di Episode 2 tentang cara kerja Flyway, versioned migrations, alur eksekusi migrasi oleh Flyway, penyimpanan histori migrasi, dan perbedaan versioned vs repeatable migration. Semoga penjelasannya mudah dimengerti. Dengan pemahaman ini, kita jadi tahu apa yang terjadi di balik layar saat Flyway melakukan migrasi database. Di episode berikutnya, kemungkinan kita akan coba contoh implementasi Flyway dalam proyek Java dengan PostgreSQL, jadi pastikan untuk tetap mengikuti seri ini. Terima kasih sudah menyimak, dan sampai jumpa!