Deployment skema database yang bergantung pada tiket manual dan pengeksekusian skrip SQL ad-hoc oleh Database Administrator (DBA) menimbulkan bottleneck rilis software. Pendekatan ini rentan terhadap human error, hilangnya audit trail, dan memicu schema drift—kondisi di mana struktur skema pada environment Development, Staging, dan Production tidak sinkron.

Oracle SQLcl menyediakan modul native Liquibase tanpa dependensi JDBC pihak ketiga tambahan. Integrasi modul ini ke dalam pipeline CI/CD memindahkan pengelolaan DDL ke sistem version control, memungkinkan inspeksi otomatis, peninjauan SQL dry-run, hingga eksekusi migrasi yang idempoten.

Struktur Direktori dan Changelog Controller

Pola manajemen objek skema membutuhkan changelog controller utama (master changelog) yang mengorganisasi rilis berdasarkan versi atau modul aplikasi. Controller ini bertindak sebagai entry-point tunggal bagi runner CI/CD.

database/
├── controller.xml
├── release-1.0/
│   ├── 001-create-orders-table.sql
│   └── 002-create-indexes.sql
└── release-1.1/
    └── 003-alter-orders-table.sql

Gunakan file XML sebagai controller root untuk mengimpor file perubahan individual. Ini memudahkan isolasi perubahan antar rilis.

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
    http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <include file="release-1.0/001-create-orders-table.sql" relativeToChangelogFile="true"/>
    <include file="release-1.0/002-create-indexes.sql" relativeToChangelogFile="true"/>
</databaseChangeLog>

Format Changeset DDL (Formatted SQL)

Meskipun format XML/YAML didukung, format SQL beranotasi Liquibase (--liquibase formatted sql) lebih praktis untuk tim engineer karena mempertahankan sintaks native Oracle PL/SQL dan DDL khusus.

--liquibase formatted sql

--changeset dimas:001-create-orders-table failOnError:true
--comment: Inisialisasi tabel orders
CREATE TABLE orders (
    order_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    customer_id NUMBER NOT NULL,
    order_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
    total_amount NUMBER(12,2) NOT NULL,
    status VARCHAR2(20) DEFAULT 'PENDING' NOT NULL
);
--rollback DROP TABLE orders CASCADE CONSTRAINTS;

--changeset dimas:002-create-orders-status-idx failOnError:true
--comment: Optimasi pencarian berdasarkan status order
CREATE INDEX idx_orders_status ON orders(status);
--rollback DROP INDEX idx_orders_status;

Setiap changeset wajib menyertakan blok --rollback untuk memastikan rollback otomatis dapat dieksekusi jika terjadi kesalahan pada rilis terkait.

Manajemen Autentikasi Aman via Oracle Wallet

Mengekspos cleartext password database dalam variabel pipeline CI/CD melanggar standar kepatuhan keamanan. Gunakan Oracle Wallet (kredensial tersimpan dalam cwallet.sso) yang dibundel dalam format ZIP dan disimpan sebagai Base64-encoded secret pada CI repository.

Encode file wallet lokal sebelum menyimpannya ke GitHub Secrets:

base64 -w 0 wallet.zip > wallet_base64.txt

Dalam runner CI, ekstrak wallet tersebut lalu konfigurasi TNS_ADMIN agar SQLcl dapat tersambung langsung menggunakan TNS Alias tanpa input password manual.

Pipeline CI/CD pada GitHub Actions

Pipeline migrasi database dijalankan bertahap: inspeksi status skema, pembuatan artifact SQL dry-run, eksekusi deployment, dan penanganan kegagalan otomatis.

name: Oracle Schema Migration

on:
  push:
    branches: [ main ]
    paths:
      - 'database/**'

jobs:
  migrate:
    runs-on: ubuntu-latest
    container:
      image: container-registry.oracle.com/database/sqlcl:latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Setup Oracle Wallet
        env:
          WALLET_BASE64: ${{ secrets.ORACLE_WALLET_BASE64 }}
        run: |
          mkdir -p /opt/oracle/wallet
          echo "$WALLET_BASE64" | base64 -d > /opt/oracle/wallet/wallet.zip
          unzip -o /opt/oracle/wallet/wallet.zip -d /opt/oracle/wallet/
          export TNS_ADMIN=/opt/oracle/wallet

      - name: 1. Inspeksi Status Skema (lb status)
        env:
          TNS_ADMIN: /opt/oracle/wallet
        run: |
          sql -s /nolog <<EOF
          connect /@app_prod_high
          lb status -changelog-file database/controller.xml -verbose
          exit;
          EOF

      - name: 2. Dry-Run SQL Preview (lb update-sql)
        env:
          TNS_ADMIN: /opt/oracle/wallet
        run: |
          sql -s /nolog <<EOF
          connect /@app_prod_high
          lb update-sql -changelog-file database/controller.xml -output-file pending_migration.sql
          exit;
          EOF

      - name: Upload Migration Preview Artifact
        uses: actions/upload-artifact@v4
        with:
          name: migration-dryrun-preview
          path: pending_migration.sql

      - name: 3. Eksekusi Deployment Skema (lb update)
        id: deploy
        env:
          TNS_ADMIN: /opt/oracle/wallet
        run: |
          sql -s /nolog <<EOF
          whenever sqlerror exit failure rollback;
          connect /@app_prod_high
          lb update -changelog-file database/controller.xml
          exit;
          EOF

      - name: Rollback Otomatis Saat Deployment Gagal
        if: failure() && steps.deploy.outcome == 'failure'
        env:
          TNS_ADMIN: /opt/oracle/wallet
        run: |
          echo "Deployment gagal. Menjalankan rollback ke tag/status sebelumnya..."
          sql -s /nolog <<EOF
          connect /@app_prod_high
          lb rollback-count -count 1 -changelog-file database/controller.xml
          exit;
          EOF

Siklus Lifecycle Perintah Liquibase di SQLcl

  1. lb status -verbose: Membaca tabel tracking DATABASECHANGELOG dan mencetak daftar changeset yang belum dieksekusi. Perintah ini mengidentifikasi item yang tertunda tanpa mengubah struktur DB.
  2. lb update-sql: Meng-generate skrip SQL mentah yang akan dieksekusi berdasarkan changeset yang tertunda. Simpan output ini sebagai artifact pipeline untuk audit kepatuhan dan validasi manual jika diperlukan.
  3. lb update: Mengeksekusi DDL pending secara atomik per changeset, memperbarui tabel DATABASECHANGELOG dengan ID, penulis, nama file, dan nilai hash (checksum).

Validasi Checksum dan Penanganan Error

1. Checksum Mismatch (MD5 Error)

Liquibase mencatat MD5 hash dari isi file changeset pada tabel DATABASECHANGELOG saat eksekusi pertama. Jika engineer mengedit file yang sudah berstatus deployed di production, runner CI akan menghentikan eksekusi dengan pesan kegagalan:

ValidationFailedException: Validation Failed:
1 change sets check sum
database/release-1.0/001-create-orders-table.sql::001-create-orders-table::dimas was: 8:e5b61f... but is now: 8:c3f12a...

Solusi: Jangan ubah file yang sudah berjalan di production. Buat changeset baru untuk memodifikasi struktur yang ada (contoh: buat 003-alter-orders-table.sql dengan DDL ALTER TABLE). Jika perubahan bersifat non-fungsional (misal: memformat spasi atau komentar), tambahkan tag <validCheckSum> atau gunakan instruksi --validCheckSum: ANY pada header changeset.

2. Strategi Rollback: Fail-Forward vs Automated Rollback

Meskipun lb rollback-count tersedia, rollback DDL otomatis pada RDBMS Oracle memiliki batasan: perintah DDL (seperti CREATE, ALTER, DROP) mengeksekusi implisit commit di Oracle. Rollback transaksi SQL biasa tidak membatalkan DDL.

Rekomendasi: Gunakan strategi fail-forward pada tabel sistem critical yang memuat data produksi. Rollback via DROP COLUMN atau DROP TABLE berisiko menghilangkan data secara permanen. Pastikan script rollback hanya dijalankan jika changeset yang gagal merupakan tabel baru yang belum menampung transaksi data.

3. Mengatasi Schema Drift

Schema drift terjadi ketika user dengan hak akses DBA melakukan modifikasi langsung pada tabel tanpa melalui Git. Ketika pipeline CI/CD dijalankan, modifikasi manual tersebut dapat bentrok dengan changeset Liquibase berikutnya.

  • Terapkan least privilege access: Akun personal DBA tidak boleh memiliki akses DDL langsung pada target schema di Production. Akses DDL hanya diberikan kepada service account yang digunakan oleh CI/CD runner.
  • Gunakan lb diff secara terjadwal pada cron job CI untuk membandingkan skema produksi terhadap master changelog dan mengirimkan alert jika terdeteksi anomali.