Skip to content

test(flyway): cover foreign-key actions across a table rebuild - #293

Open
magnusp wants to merge 1 commit into
mainfrom
test/flyway-fk-table-rebuild
Open

magnusp wants to merge 1 commit into
mainfrom
test/flyway-fk-table-rebuild

Conversation

@magnusp

@magnusp magnusp commented Nov 1, 2026

Copy link
Copy Markdown
Owner

What

Adds a generalized Flyway integration test for a downstream report: a seeding migration appeared to lose a child row while Flyway reported success, with PRAGMA foreign_keys = ON on every connection.

The sample separates three shapes, all through this driver to a real sqld container:

  1. Seeding a parent in one migration and two children in a later one, with INSERT OR IGNORE and plain INSERT, keeps every row.
  2. A later migration that rebuilds a parent table with copy, drop, rename removes the child rows. DROP TABLE runs an implicit DELETE FROM, and ON DELETE CASCADE fires. That is SQLite's documented behavior, and it reproduces the downstream symptom without a driver bug.
  3. With a child foreign key that has no cascade, the same rebuild fails with FOREIGN KEY constraint failed, which pins that enforcement is active for every connection Flyway takes.

FlywaySqldSupport now takes the foreign_keys pragma value instead of a boolean, so the enabled and disabled cases share one code path.

Why

  • Pins foreign-key enforcement across Flyway's migration transaction, which the existing FlywayKnownGapsIT only covers for the disabled case.
  • Documents the trap for whoever writes a SQLite table rebuild: a DROP TABLE under foreign keys fires ON DELETE actions, so a migration that rebuilds a referenced table can remove child rows unless the rebuild runs with foreign keys off.

Test

DOCKER_CONFIG="$PWD/libsql-conformance/src/test/docker-config" \
  ./mvnw -pl libsql-jdbc verify

All libsql-jdbc unit and integration tests pass, including the new FlywayForeignKeySeedIT against the pinned v0.24.33 image. Also run against -Dlibsql.image=ghcr.io/tursodatabase/libsql-server:latest locally, with the same result.

Notes

The report came from a Spring Boot application using a Hikari pool with connection-init-sql: PRAGMA foreign_keys = ON. That setup is not part of this test; the DataSource wrapper sets the same pragma before Flyway takes the connection, which is the property that matters. A pooled variant can be added to libsql-itests if there is appetite for a Hikari dependency in these Flyway tests.

A downstream Spring Boot application reported a fixture row that vanished
while Flyway reported success, with PRAGMA foreign_keys = ON for every
connection. The sample separates the two shapes involved:

- seeding a parent and then INSERT OR IGNORE children across migrations
  keeps every row;
- a later migration that rebuilds a parent table with copy, drop, rename
  removes the child rows, because DROP TABLE runs an implicit DELETE FROM
  and ON DELETE CASCADE fires.

The cascade is SQLite's documented behavior, not row loss in the driver.
With a child foreign key that has no cascade, the same rebuild fails with
FOREIGN KEY constraint failed, which pins that enforcement is active for
every connection Flyway takes.

FlywaySqldSupport now takes the foreign_keys pragma instead of a boolean,
so the enabled and disabled cases share one code path.
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

Performance benchmark

Base 2769e69feea9 → head 9673c9d9763e. Medians of 3 base runs and 3 head runs, interleaved on the same runner.

Driver-side microbenchmarks only; server and network time are excluded. Shared-runner noise is about ±10%, so only larger regressions are flagged.

JDK 25.0.5, Linux amd64, 4 CPUs.

Throughput

Workload Base Head Δ
Decode 5k rows (direct) 1.06M rows/s 1.05M rows/s -1.6%
Decode 5k rows (Jackson baseline) 906.0k rows/s 929.4k rows/s +2.6%
Decode 20k rows (direct) 1.03M rows/s 1.03M rows/s -0.9%
Encode one execute (direct) 989.2k req/s 940.1k req/s -5.0%
Encode one execute (Jackson baseline) 938.2k req/s 949.2k req/s +1.2%
JDBC scan + getters (20k rows) 27.50M rows/s 27.01M rows/s -1.8%
JDBC scan + getObject (20k rows) 13.13M rows/s 13.73M rows/s +4.6%

Allocation

Workload Base Head Δ
Decode 5k rows (direct) 8.18 MB/op 8.18 MB/op +0.0%
Decode 5k rows (Jackson baseline) 5.22 MB/op 5.22 MB/op +0.0%
Decode 20k rows (direct) 32.66 MB/op 32.66 MB/op +0.0%
Encode one execute (direct) 1.38 kB/op 1.38 kB/op +0.0%
Encode one execute (Jackson baseline) 1.84 kB/op 1.84 kB/op +0.0%
JDBC scan + getters (20k rows) 1.04 kB/op 1.04 kB/op +0.0%
JDBC scan + getObject (20k rows) 1.12 MB/op 1.12 MB/op +0.0%

Workloads and thresholds live in tools/perf/render-comment.py. Run locally with ./mvnw -pl libsql-perf -am -DskipTests package and see libsql-perf/README.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant