Database Migration
Runbook penggunaan sistem migration AlurKerja — generate migration dari selisih skema (schema-diff) lalu apply ke database tujuan dengan runner release-aware ala Laravel. Termasuk cara pengecekan dan expected result dari simulasi end-to-end.
A. Ringkasan & Definition of Done
Sistem migration AlurKerja terdiri dari dua alat yang terpisah tugasnya:
| Alat | Perintah | Tugas |
|---|---|---|
| schema-diff | make schema-diff | Menghasilkan file migration dari selisih skema baseline vs DB server. |
| migrate | make migrate | Menerapkan file migration ke database tujuan dan mencatat versinya. |
Halaman ini fokus pada penggunaan kedua alat — terutama runner migrate — sampai cara pengecekan dan expected result yang sudah divalidasi lewat simulasi.
Model pelacakannya mengikuti migration Laravel: setiap file migration dicatat per nama, lalu dikelompokkan ke dalam release (padanan konsep batch di Laravel). Satu release = satu unit rollback.
Definition of Done (DoD) — migration dianggap selesai bila:
make migrateselesai tanpa error dan menampilkanSelesai. N migration baru ter-apply(atauNothing to migratebila memang sudah sinkron).make migrate-statusmenampilkan seluruh migration dalam status[x] ter-apply,0 menunggu.- Jumlah objek (tabel/view/function) di database tujuan sesuai dengan yang diharapkan.
schema-diff dan migrate berada di repository alurkerja-release pada folder scripts/. Semua contoh command di halaman ini dijalankan dari root repository tersebut.
B. Prasyarat
Langkah 1–3 memastikan environment siap. Jangan lanjut sebelum semua terpenuhi.
1. Verifikasi klien psql terpasang
Aksi
psql --versionExpected output
psql (PostgreSQL) 14.x atau lebih baru.
Jika gagal: install paket klien PostgreSQL (
postgresql-clientdi Debian/Ubuntu,brew install libpqdi macOS).
2. Verifikasi akses ke database tujuan
Aksi
PGPASSWORD='<DB_PASS>' psql -X "host=<DB_HOST> port=<DB_PORT> user=<DB_USER> dbname=<DB_NAME>" -tAc 'SELECT 1'Expected output
Angka 1 tercetak tanpa error.
Jika gagal: pastikan host/port benar, kredensial valid, dan (bila database di jaringan internal) VPN aktif.
3. Verifikasi folder migration tersedia
Aksi
ls migrations/*/*/ | headExpected output
Terlihat folder per schema dan per release, mis. migrations/public/2026.07.3.1/ berisi file *.up.sql dan *.down.sql.
Jika kosong: file migration belum di-generate. Jalankan
make schema-difflebih dulu (lihat halaman schema-diff).
C. Konsep: struktur folder & tabel pelacak
Struktur folder
File migration ditata per schema, lalu per release:
migrations/
public/
2026.01.1.1/ baseline — dari config/public.sql (1 file)
2026.07.3.1/ release — selisih baseline vs server (31 file per konteks)
camunda/
2026.01.1.1/ baseline — dari config/camunda.sql
2026.07.3.1/ release — hanya tabel act_* + trigger (3 file)Setiap migration punya pasangan *.up.sql (maju) dan *.down.sql (rollback). Nama file berprefix timestamp 14 digit, mis. 20260716090100_create_access_logs.up.sql.
Tabel pelacak migrate.migrations
Runner mencatat migration yang sudah diterapkan di tabel berikut, dibuat otomatis di schema terpisah migrate (bukan public):
CREATE TABLE migrate.migrations (
id bigserial PRIMARY KEY,
schema_name text NOT NULL, -- 'public' | 'camunda'
migration text NOT NULL, -- '20260716090100_create_access_logs'
release text NOT NULL, -- '2026.07.3.1' ← "batch" ala Laravel
applied_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (schema_name, migration)
);Tabel pelacak sengaja ditaruh di schema migrate, bukan public. Baseline public menjalankan CREATE SCHEMA public yang destruktif — bila tabel pelacak berada di public, ia akan ikut terhapus saat skema dibangun ulang.
Tiga aturan yang wajib dipegang
- Pelacakan berbasis
(schema, migration). Timestamp restart di tiap schema (public dan camunda bisa punya20260716090000yang sama), jadi nama migration hanya unik di dalam satu schema. Runner sudah menangani ini; jangan pernah menggabung (flatten) kedua schema ke satu folder. - Urutan schema:
publicdulu, barucamunda. camunda memiliki foreign key kepublic(mis.camunda.tenant_certificates→public.tenants). Runner menerapkannya dalam urutan ini; rollback memakai urutan terbalik. - Release yang sudah deploy = beku. Jangan pernah regenerate release yang sudah diterapkan di server. Perubahan berikutnya masuk ke release baru (
make schema-diffotomatis menaikkan nomor release).
D. Konfigurasi koneksi
Runner membaca koneksi dari scripts/migrate.env (di-gitignore, tidak ikut ter-commit).
4. Siapkan scripts/migrate.env
Aksi
cp scripts/migrate.env.example scripts/migrate.env
# lalu edit isinyaIsi minimal:
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASS=<password>
DB_NAME=<nama-database>
# Urutan schema WAJIB benar: public dulu (camunda punya FK ke public).
SCHEMAS=public camundaJangan pernah commit scripts/migrate.env — file ini berisi password. File sudah terdaftar di .gitignore.
E. Perintah yang tersedia
| Perintah | Alias make | Fungsi |
|---|---|---|
./scripts/migrate.sh up | make migrate | Apply semua *.up.sql yang belum tercatat (public → camunda). |
./scripts/migrate.sh status | make migrate-status | Daftar migration beserta status ter-apply / menunggu. |
./scripts/migrate.sh rollback | make migrate-rollback | Mundurkan release terakhir (semua schema, urutan terbalik). |
./scripts/migrate.sh unlock | — | Lepas paksa lock sisa proses yang mati. |
Sifat penting runner:
- Idempoten — migration yang sudah tercatat otomatis dilewati;
upaman dijalankan berulang. - Atomik per file — tiap
*.up.sqlditerapkan denganpsql --single-transactiondigabung dengan pencatatannya dalam satu transaksi. Bila gagal di tengah, seluruhnya di-rollback dan tidak tercatat, sehingga cukup dijalankan ulang setelah masalah diperbaiki. - Terkunci — dua proses
up/rollbacktidak bisa jalan bersamaan di database yang sama (lock berbasis baris, dengan pembersihan lock basi otomatis).
F. Simulasi end-to-end & expected result
Berikut simulasi lengkap yang sudah divalidasi pada Postgres lokal (container some-postgres, PostgreSQL 18 dengan pgvector) menggunakan database uji alur_migrate_test. Ikuti untuk memastikan sistem berfungsi sebelum dipakai ke database sungguhan.
Simulasi ini memakai database khusus uji alur_migrate_test yang dibuat kosong lalu dihapus lagi. Jangan menjalankan langkah 5 pada database berisi data.
5. Siapkan database uji kosong lalu apply (up)
Aksi
# 5a. buat database uji kosong
PGPASSWORD='<DB_PASS>' psql -X "host=<DB_HOST> port=<DB_PORT> user=<DB_USER> dbname=postgres" \
-c "DROP DATABASE IF EXISTS alur_migrate_test WITH (FORCE)" \
-c "CREATE DATABASE alur_migrate_test"
# 5b. buang schema public bawaan — baseline public menjalankan CREATE SCHEMA public
PGPASSWORD='<DB_PASS>' psql -X "host=<DB_HOST> port=<DB_PORT> user=<DB_USER> dbname=alur_migrate_test" \
-c "DROP SCHEMA IF EXISTS public CASCADE"
# 5c. terapkan seluruh migration
DB_NAME=alur_migrate_test ./scripts/migrate.sh upExpected output
Runner menerapkan seluruh migration berurutan — public dulu (baseline + release), baru camunda:
==> migrate up (postgres@localhost:5432/alur_migrate_test)
public 2026.01.1.1 20260101000000_baseline_public_schema ... OK
public 2026.07.3.1 20260716090000_enable_extensions_and_types ... OK
public 2026.07.3.1 20260716090100_create_access_logs ... OK
... (31 migration release public) ...
camunda 2026.01.1.1 20260101000000_baseline_camunda_schema ... OK
camunda 2026.07.3.1 20260716090000_add_constraints_and_indexes_to_existing_tables ... OK
camunda 2026.07.3.1 20260716090100_create_functions_views_triggers ... OK
camunda 2026.07.3.1 20260716090200_review_manual ... OK
Selesai. 36 migration baru ter-apply.Yang harus benar: total 36 migration ter-apply — 32 public (1 baseline + 31 release) dan 4 camunda (1 baseline + 3 release).
Langkah 5a–5b (drop public bawaan) hanya diperlukan saat membangun database dari nol, karena baseline public menjalankan CREATE SCHEMA public. Pada database yang sudah berisi skema, cukup jalankan up (langkah 5c) tanpa menyentuh schema.
Jika gagal di tengah: migration yang gagal di-rollback (transaksi tunggal) dan tidak tercatat. Perbaiki penyebabnya, lalu jalankan
./scripts/migrate.sh up— yang sudah sukses tidak diulang.
6. Uji idempotensi (up kedua kali)
Aksi
DB_NAME=alur_migrate_test ./scripts/migrate.sh upExpected output
==> migrate up (postgres@localhost:5432/alur_migrate_test)
Nothing to migrate. Semua sudah ter-apply.Menjalankan
upberkali-kali aman dan tidak mengubah apa pun bila skema sudah sinkron.
7. Periksa status
Aksi
DB_NAME=alur_migrate_test ./scripts/migrate.sh statusExpected output (dipersingkat)
==> status (alur_migrate_test)
public/2026.01.1.1
[x] 20260101000000_baseline_public_schema
public/2026.07.3.1
[x] 20260716090000_enable_extensions_and_types
[x] 20260716090100_create_access_logs
...
camunda/2026.07.3.1
[x] 20260716090100_create_functions_views_triggers
[x] 20260716090200_review_manual
36 ter-apply, 0 menunggu.Yang harus benar: 36 ter-apply, 0 menunggu — semua migration bertanda [x].
G. Cara pengecekan (verifikasi hasil)
Selain status, verifikasi langsung ke database bahwa objek benar-benar terbentuk.
8. Hitung objek per schema
Aksi
PGPASSWORD='<DB_PASS>' psql -X "host=<DB_HOST> port=<DB_PORT> user=<DB_USER> dbname=alur_migrate_test" -c "
SELECT n.nspname AS schema,
count(*) FILTER (WHERE c.relkind='r') AS tabel
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname IN ('public','camunda')
GROUP BY n.nspname ORDER BY 1;"Expected output
schema | tabel
---------+-------
camunda | 49
public | 109Yang harus benar: public 109 tabel, camunda 49 tabel (persis 49 tabel act_* Camunda).
9. Periksa isi tabel pelacak per release
Aksi
PGPASSWORD='<DB_PASS>' psql -X "host=<DB_HOST> port=<DB_PORT> user=<DB_USER> dbname=alur_migrate_test" -c "
SELECT schema_name, release, count(*)
FROM migrate.migrations GROUP BY 1,2 ORDER BY 1,2;"Expected output
schema_name | release | count
-------------+-------------+-------
camunda | 2026.01.1.1 | 1
camunda | 2026.07.3.1 | 3
public | 2026.01.1.1 | 1
public | 2026.07.3.1 | 31H. Rollback
10. Mundurkan release terakhir
Aksi
DB_NAME=alur_migrate_test ./scripts/migrate.sh rollbackExpected output (dipersingkat)
==> rollback release 2026.07.3.1 (alur_migrate_test)
camunda 20260716090200_review_manual ... OK
camunda 20260716090100_create_functions_views_triggers ... OK
camunda 20260716090000_add_constraints_and_indexes_to_existing_tables ... OK
public 20260716093000_review_manual ... OK
... (release public, urutan mundur) ...
public 20260716090000_enable_extensions_and_types ... OK
Selesai. 34 migration di release 2026.07.3.1 di-rollback.Yang harus benar:
- 34 migration release
2026.07.3.1di-rollback (31 public + 3 camunda) — urutan terbalik: camunda dulu, lalu public dari timestamp terbesar ke terkecil. - Setelah rollback,
statusmenunjukkan2 ter-apply, 34 menunggu(hanya dua baseline yang tersisa). - Hitung objek kembali ke kondisi baseline: public 60 tabel (turun dari 109), camunda 49 (release camunda hanya menambah constraint/trigger, bukan tabel, sehingga jumlah tabel tidak berubah).
rollback selalu menyasar release terakhir yang tercatat (ditentukan lewat urutan versi, bukan leksikografis). Jalankan berulang untuk mundur release demi release.
I. Troubleshooting
| Gejala | Penyebab | Aksi |
|---|---|---|
Runner lain sedang jalan (lock dipegang: ...) | Proses up/rollback sebelumnya mati sebelum melepas lock. | Pastikan tidak ada proses lain berjalan, lalu ./scripts/migrate.sh unlock. Lock lebih tua dari LOCK_TIMEOUT (default 2 jam) dibersihkan otomatis. |
Migration gagal di tengah up | Error SQL pada satu file. | File itu di-rollback (transaksi tunggal), tidak tercatat. Perbaiki penyebab lalu up lagi — yang sudah sukses dilewati. |
schema "public" already exists saat up di DB kosong | Baseline public menjalankan CREATE SCHEMA public, sementara DB baru sudah punya public bawaan. | Buang schema public bawaan sebelum up: psql ... -c "DROP SCHEMA IF EXISTS public CASCADE" (lihat langkah 5b). |
extension "vector" is not available | Server AlurKerja memakai extension vector (kolom knowledge_chunks.embedding); image Postgres polos tidak membawanya. | Pakai image ber-pgvector (mis. pgvector/pgvector:pg18) atau install paket postgresql-<versi>-pgvector. |
J. Catatan untuk database produksi
Jangan menjalankan langkah penyiapan DB kosong (langkah 5a–5b) pada database produksi — DROP DATABASE/DROP SCHEMA public akan menghapus seluruh data. Pada produksi cukup jalankan up.
Untuk server on-premise yang sudah berisi skema (sudah pada kondisi baseline), file baseline tidak boleh diterapkan ulang — cukup terapkan release baru saja. Alur amannya:
- Tandai baseline sebagai sudah-applied di tabel
migrate.migrationstanpa menjalankan SQL-nya (agaruphanya menerapkan release di atas baseline). - Jalankan
make migrate-statusuntuk memastikan hanya release baru yang berstatusmenunggu. - Jalankan
make migrate.
Subcommand khusus untuk menandai baseline sebagai sudah-applied sedang disiapkan. Sampai tersedia, lakukan langkah 1 secara manual bersama tim yang menyiapkan runner sebelum menjalankan up ke server produksi yang sudah hidup.
Health Monitoring
Implementasi health monitoring di Alurkerja
Penggabungan Websocket Kolaborasi ke Notification Service
Mulai versi 2026.08.1.1, websocket kolaborasi BPMN (Yjs) digabung ke dalam service notifikasi. Halaman ini menjelaskan apa yang berubah dan langkah update yang perlu dilakukan pada deployment on-premise Anda.
