This guide explains how Aviate manages database schema migrations and how to validate that migrations have been applied successfully.
For plugin installation steps, see How to Install the Aviate Plugin.
By default, Aviate runs database migrations automatically at startup. This behavior is controlled by the following property:
com.killbill.billing.plugin.aviate.enableMigrations=true
When enabled, Flyway initializes the Aviate schema and applies all pending migrations during plugin startup.
How Aviate Manages Database Migrations
Aviate uses Flyway to version and apply database schema changes.
At startup, Flyway:
-
Creates the
aviate_schema_historytable if it does not already exist -
Applies migrations in version order
-
Tracks applied migrations to prevent duplicate or partial execution
Migration Scenarios
Scenario 1: Fresh Install (No Existing Aviate Schema)
This scenario applies to brand-new installations where Aviate has never been started against the target database.
Symptoms
-
No
aviate_*tables exist. -
The
aviate_schema_historytable is not present.
Expected Action
Start the Aviate plugin. Flyway will automatically:
-
Create the schema history table
-
Apply all migrations
-
Initialize the Aviate schema
No manual intervention is required.
What to Verify
After the plugin starts successfully:
-
Confirm that the schema history table exists:
---- SHOW TABLES LIKE 'aviate_schema_history'; ----
-
Verify that migrations were applied:
---- SELECT version, description, success FROM aviate_schema_history ORDER BY installed_rank DESC; ----
All rows should show:
---- success = 1 ----
-
Confirm that the expected
aviate_*tables are present.
Expected Logs
Look for log entries similar to the following:
Migrations are enabled. Starting migration process... Schema history table `killbill`.`aviate_schema_history` does not exist yet Successfully validated 16 migrations (execution time 00:00.026s) Successfully applied 16 migrations to schema `killbill`, now at version v1.16.0 (execution time 00:07.871s) Migration process completed successfully
Scenario 2 — Existing Aviate Schema but No aviate_schema_history (Adopting into Flyway)
This scenario occurs when Aviate tables already exist in the database, but Flyway has never been used to track migrations.
This is common when the schema was created manually.
Symptoms
-
aviate_*tables exist. -
The
aviate_schema_historytable is missing.
Failure Mode
When the plugin starts, Flyway assumes the database is empty and attempts to run the initial migrations.
Since the tables already exist, the migration fails with errors similar to:
Schema history table `killbill`.`aviate_schema_history` does not exist yet Creating Schema History table `killbill`.`aviate_schema_history` with baseline ... Migrating schema `killbill` to version "1.1.0 - Initial version" Error: 1050-42S01: Table 'aviate_hosts' already exists
Subsequent restarts continue to fail because Flyway retries the same migration.
Root Cause
Flyway has no record of previously applied migrations and cannot determine the current schema version.
The database must be baselined so Flyway can begin tracking migrations from the correct version.
Selecting the wrong baseline version can cause Flyway to either:
-
Re-run migrations against existing objects, or
-
Skip required migrations, leading to runtime failures.
Always validate the baseline version before proceeding.
Resolution — Align Flyway with the Existing Schema (Baselining)
When migrations are enabled (com.killbill.billing.plugin.aviate.enableMigrations=true), Flyway automatically creates the aviate_schema_history table and attempts to apply migrations.
If the schema already exists, Flyway must be aligned with the current database state by setting the correct baseline.
Step 1 — Retrieve the Migration Files
Download the migration scripts from the following link:
MySQL migrations: https://docs.killbill.io/latest/aviate-migrations/mysql/
PostgreSQL migrations: https://docs.killbill.io/latest/aviate-migrations/postgresql/
Migration files follow the following format:
aviateV1.1.0__Initial_version.sql aviateV1.2.0__catalog_usage.sql aviateV1.3.0__catalog_meter.sql
After downloading, compare the migration files with your current Aviate schema to identify the latest version already reflected in your database. This version will be used as the Flyway baseline.
Example:
aviateV1.8.0__notifications.sql
✅ Baseline version = 1.8.0
Step 2 — Check for Failed Migrations
SELECT * FROM aviate_schema_history;
If a row shows:
success = 0
remove only that failed entry:
DELETE FROM aviate_schema_history WHERE success = 0;
Step 3 — Insert the Correct Baseline
Insert a row matching the schema already present in the database.
Example:
INSERT INTO aviate_schema_history (installed_rank, version, description, type, script, installed_by, execution_time, success) VALUES (2, '1.8.0', '<< Flyway Baseline >>', 'BASELINE', 'aviateV1.8.0__notifications.sql', CURRENT_USER(), 0, 1);
Step 4 — Restart the Aviate Plugin
On startup:
-
Flyway detects the baseline.
-
Older migrations are skipped.
-
Only newer migrations are applied.
Verification
SELECT installed_rank, version, success FROM aviate_schema_history;
Confirm:
-
No rows with success = 0
-
Baseline version is present
-
New migrations complete successfully
Scenario 3 — Upgrade Aviate Plugin When Flyway Is Already Managing the Schema
Symptoms
-
The
aviate_schema_historytable exists. -
Plugin starts without migration failures.
-
No rows in
aviate_schema_historyshow success = 0.
Action
Upgrade the Aviate plugin using the standard uninstall → install workflow.
Uninstalling the plugin does not remove any aviate_* tables or the schema history table.
Uninstall the Existing Plugin
Send a POST request to /1.0/kb/nodesInfo, here is a sample request to uninstall a plugin
curl -v \
-u admin:<password> \
-H "Content-Type: application/json" \
-H 'X-Killbill-CreatedBy: admin' \
-X POST \
--data-binary '{
"nodeCommandProperties": [
{
"key": "pluginKey",
"value": "aviate"
},
{
"key": "pluginVersion",
"value": "<current-version>"
}
],
"nodeCommandType": "UNINSTALL_PLUGIN",
"isSystemCommandType": true
}' \
"http://127.0.0.1:8080/1.0/kb/nodesInfo"
Expected Logs
Starting uninstallation of plugin: pluginKey=aviate, version=1.0.29 Deleted plugin directory: /var/lib/killbill/bundles/plugins/java/aviate-plugin/1.0.29 Unregistering service='aviate-plugin' Successfully uninstalled plugin: pluginKey=aviate, version=1.0.29
Install the Upgraded Plugin
Sample request to install an Aviate plugin
curl -v \
-u admin:password \
-H "Content-Type: application/json" \
-H 'X-Killbill-CreatedBy: admin' \
-X POST \
--data-binary '{
"nodeCommandProperties": [
{
"key": "pluginKey",
"value": "aviate"
},
{
"key": "pluginVersion",
"value": "<new-version>"
},
{
"key": "pluginArtifactId",
"value": "aviate-plugin"
},
{
"key": "pluginGroupId",
"value": "com.kill-bill.billing.plugin.java"
},
{
"key": "pluginType",
"value": "java"
},
{
"key": "pluginUri",
"value": "https://dl.cloudsmith.io/<token>/killbill/aviate/maven/com/kill-bill/billing/plugin/java/aviate-plugin/<new-version>/aviate-plugin-<version>.jar"
}
],
"nodeCommandType": "INSTALL_PLUGIN",
"isSystemCommandType": "true"
}' \
"http://127.0.0.1:8080/1.0/kb/nodesInfo"
What to Verify
After installation:
-
The plugin starts successfully.
-
Flyway appends new rows to
aviate_schema_history. -
No rows in
aviate_schema_historyshow success = 0. -
No migration errors appear in the logs.
Scenario 4 — Failed Migration Recorded in aviate_schema_history (success = 0)
Symptoms
-
Plugin startup fails with errors such as:
"Detected failed migration to version 1.7.0 (ledger)." "FlywayValidateException: Validate failed: Migrations have failed validation"
-
The latest row in
aviate_schema_historyshows:
success = 0
Root Cause
A Flyway migration started but did not complete successfully.
Although the Aviate plugin automatically attempts a Flyway repair, the repair only fixes the schema history table — it does not undo partially applied database changes.
Manual cleanup is often required before the migration can succeed.
Action — Correct Remediation Flow
Step 1 — Identify the Failed Migration
SELECT * FROM aviate_schema_history ORDER BY installed_rank DESC;
Note the migration version where success = 0.
Step 2 — Inspect Partial Changes
Review the migration script referenced in the error logs.
Download the migration scripts from the following link:
MySQL migrations: https://docs.killbill.io/latest/aviate-migrations/mysql/
PostgreSQL migrations: https://docs.killbill.io/latest/aviate-migrations/postgresql/
Open the migration file mentioned in the logs to understand what schema changes were attempted.
Example:
Migration aviateV1.7.0__ledger.sql failed Table 'aviate_wallets' already exists
Determine whether the migration:
-
Created some tables or columns
-
Added indexes
-
Modified constraints
Step 3 — Revert or Align the Database
Bring the database to the expected pre-migration state.
Typical fixes include:
-
Dropping partially created tables
-
Removing incomplete indexes
-
Reverting schema alterations
Example:
DROP TABLE aviate_wallets;
Step 4 — Restart the Aviate Plugin
On restart:
-
Aviate triggers Flyway.
-
Flyway validates the schema.
-
The repaired migration is executed again.
What to Verify
After startup:
-
No migration errors appear in logs.
-
The previously failed migration now shows:
success = 1
-
New migrations continue normally.
Expected Logs:
FlywayValidateException: Validate failed: Migrations have failed validation Successfully repaired schema history table `killbill`.`aviate_schema_history` (execution time 00:00.017s). Flyway repair completed. Retrying migration... Successfully applied 2 migrations to schema `killbill`, now at version v1.8.0 (execution time 00:00.536s) Migration completed successfully after repair Migration process completed successfully
Scenario 5 — Schema Drift or Checksum Mismatch
Symptoms
-
Aviate plugin fails during startup.
-
Flyway validation reports checksum mismatch or indicates that a migration was applied but differs from the current script.
-
Flyway or database errors reference missing columns, tables, or altered objects, for example:
Migration of schema `killbill` to version "1.10.0 - invoice sequence rename columns" failed! Please restore backups and roll back database and code! Error: 1054-42S22: Unknown column 'kb_account_id' in 'aviate_invoice_sequences' SQL State : 42S22 Error Code : 1054 Message : (conn=10) Unknown column 'kb_account_id' in 'aviate_invoice_sequences' Location : db/migration/mysql/aviateV1.10.0__invoice_sequence_rename_columns.sql (/db/migration/mysql/aviateV1.10.0__invoice_sequence_rename_columns.sql) Line : 7
Root Cause
The database schema does not match the migration history recorded by Flyway.
This typically occurs when:
-
Migration scripts were modified after being applied.
-
A different plugin artifact was deployed against an existing database.
-
Manual schema changes were made outside Flyway.
-
The database was restored from a snapshot that does not align with the plugin version.
Flyway validates migration checksums but cannot detect all forms of schema drift, which can lead to runtime failures even when validation succeeds.
Resolution — Realign Schema with Migration History
Step 1 — Identify the Expected Migration
Review the error logs to determine which column, table, or object is missing or inconsistent.
Example:
Unknown column 'kb_account_id'
This indicates that the migration responsible for adding this column was never applied or was reverted.
Step 2 — Check the Migration Script
Download the migration scripts from the following link:
MySQL migrations: https://docs.killbill.io/latest/aviate-migrations/mysql/
PostgreSQL migrations: https://docs.killbill.io/latest/aviate-migrations/postgresql/
Locate the migration file referenced in the error logs and review it to determine which column, table, or object is missing from the schema.
Step 3 — Verify Schema vs Migration History
Confirm whether the change exists in the database:
SHOW COLUMNS FROM aviate_notifications;
Possible outcomes:
Column missing
→ The migration was never applied, even though Flyway believes it was.
Column exists but differs
→ The schema was manually altered.
Step 4 — Choose the Correct Remediation Path
If the migration should have run but did not:
Apply the schema change manually using the migration script, then restart the plugin.
If migration scripts were modified after deployment:
Restore the original migration files that match the recorded checksums.
If manual database changes caused the drift:
Revert the schema to match the migration.
Scenario 6 — Incorrect Baseline Version (Too Low or Too High)
Symptoms
Baseline Too Low
Flyway attempts to re-run migrations that were already applied, resulting in errors such as:
FlywayMigrateException: Migration aviateV1.3.0__catalog_meter.sql failed SQL State : 42S01 Error Code : 1050 Message : Table 'aviate_billing_meters' already exists
Baseline Too High
Flyway skips required migrations because it assumes the schema is newer than it actually is. The plugin may start, but runtime failures occur due to missing objects.
Example:
Current version of schema `killbill`: 1.15.0 Migrating schema `killbill` to version "1.16.0" Migration process completed successfully org.jooq.exception.DataAccessException: SQL [insert into aviate_health_reports (creating_owner, report_data_gz, created_date, updated_date) values (?, ?, ?, ?) on duplicate key update aviate_health_reports.report_data_gz = ?, aviate_health_reports.updated_date = ?]; (conn=45) Table 'killbill.aviate_health_reports' doesn't exist Caused by: java.sql.SQLSyntaxErrorException: (conn=45) Table 'killbill.aviate_health_reports' doesn't exist
Root Cause
The baseline version recorded in aviate_schema_history does not accurately represent the actual database schema.
-
Too low → Flyway replays migrations.
-
Too high → Flyway skips migrations that were never applied.
Recommended Resolution (Safest Approach)
Restore the database from a known good backup and repeat the baselining process using the correct migration version.
This is the lowest-risk recovery strategy for production systems.
Surgical Fix (Advanced — Use With Caution)
Only perform this procedure if:
-
A database backup exists.
-
The schema state has been carefully verified.
-
You fully understand which migrations have actually been applied.
Step 1 — Stop Kill Bill
Prevent additional migrations or schema changes while correcting the baseline.
Step 2 — Identify the Correct Migration Version
Inspect the plugin migration scripts:
db/migration/mysql or db/migration/postgresql
Compare them with the database schema to determine the latest migration already reflected in the database.
Indicators:
-
"already exists" → baseline is too low
-
Missing table/column → baseline is too high
Step 3 — Correct the Baseline Entry
Check the current history:
SELECT * FROM aviate_schema_history ORDER BY installed_rank;
Update the baseline by removing the incorrect entry:
DELETE FROM aviate_schema_history WHERE version = '<incorrect_version>';
Insert the correct baseline:
INSERT INTO aviate_schema_history (installed_rank, version, description, type, script, installed_by, execution_time, success) VALUES (<next_rank>, '<correct_version>', '<< Flyway Baseline >>', 'BASELINE', 'aviateV<correct_version>__<description>.sql', CURRENT_USER(), 0, 1);
Step 4 — Restart the Aviate Plugin
On startup:
-
Flyway validates the schema.
-
Only missing migrations are applied.
-
Duplicate migrations are skipped.
Scenario 7 — Concurrent Startup (Multiple Aviate Nodes Running Migrations)
Symptoms
-
Aviate plugin startup fails intermittently.
-
Flyway reports lock wait timeouts, deadlocks, or messages such as:
Schema history table is being modified
-
One node starts successfully while others fail during migration.
Root Cause
Multiple Aviate nodes attempt to execute Flyway migrations at the same time. Although Flyway uses a schema history lock, certain database setups (for example, proxies or misconfigured clusters) can interfere with proper locking.
Resolution
-
Run migrations from a single node
Ensure only one Aviate instance performs migrations during deployment.
Common approaches include:
-
Temporarily scale the deployment to one node.
-
Disable the Aviate plugin on other nodes until migration completes.
-
Use a dedicated migration job before bringing all nodes online.
-
-
Verify database locking behavior
Confirm that Flyway can acquire an exclusive lock on the Aviate schema history table.
Check for environments that may weaken locking, such as:
-
Read replicas being used unintentionally for migrations.
-
Database proxies or load balancers routing connections to different writers.
-
Cluster configurations without proper write coordination.
-
-
Restart remaining nodes
After migrations complete successfully, start the other Aviate nodes.
Prevention
-
Execute migrations as part of a controlled deployment step before scaling services.
-
Avoid parallel application startups when schema changes are included.
-
Validate database topology so migrations always run against the primary writable instance.