Skip to content

fix(conformance): close leaked namespaces in the harness - #134

Merged
magnusp merged 2 commits into
mainfrom
fix/i120-conformance-proxy
Oct 22, 2026
Merged

magnusp merged 2 commits into
mainfrom
fix/i120-conformance-proxy

Conversation

@magnusp

@magnusp magnusp commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Fixes #120. Part of #54.

What changed

  • Wrap Statement.getConnection() and DatabaseMetaData.getConnection() so both return the same connection proxy. getConnection() == conn holds, and closing through either releases the namespace once.
  • Track the underlying connection per namespace. XerialTestServer.stop() now fails while any namespace still has an open connection, and reports namespaces whose cleanup failed instead of clearing them silently. An escaped raw connection closed through unwrap is reconciled before the leak check.
  • Parse SQLite file: URIs. file::memory:?cache=shared and ?mode=memory map to fresh per-connection namespaces instead of retained file namespaces. The query string is part of a file namespace's identity, so different options address different databases.
  • Add XerialHarnessTest with eight self-tests: wrapped identity through statements and metadata, release-once, stop() failure on a leaked connection, swallowed cleanup-failure reporting, file-memory isolation, query-string isolation, and unwrap identity.

No production driver code changed.

Contract

Hrana 3 is the transport and value source. java.sql defines the connection and wrapper surface. xerial/sqlite-jdbc remains the conformance reference. The query string is namespace identity only because sqld exposes no URL pragma surface; file::memory:?cache=shared is isolated per connection rather than shared across them.

Divergences

  • The JSON1Test and CachedRowSetTest connection closes stay in the vendored copies. Those tests leak top-level connections, which the stricter stop() must reject. The new wrapper handles connections reached through statements and metadata, not a top-level leak whose reference the test still owns.
  • The manifest and checksums are unchanged. No vendored file was edited.

Files outside the plan block

  • libsql-conformance/src/test/java/com/github/magnusp/libsql/conformance/XerialHarnessTest.java is the harness self-test the plan's unit box requires.

Evidence

Base 0e83a5793020d09fa58581477ca9a73ac7bb593e, head 12d2c8e0d4bc89927f50ce24d55f45199d8caae3.

Red:

  • ./mvnw -pl libsql-conformance -am test-compile failed: the old harness had no plan or stopForTest seam, and the old mapping treated file::memory:?cache=shared as a retained file path and ignored query strings. Log: /tmp/xerial-parity-i120/red-compile.log.

Green:

  • ./mvnw -pl libsql-conformance -am verify -> 123 tests, 0 failures, 1 skipped (/tmp/xerial-parity-i120/lane-2-specific.log).
  • ./mvnw -pl libsql-conformance -am -Pxerial-control verify -> 100 tests, 0 failures, 1 skipped (/tmp/xerial-parity-i120/lane-5-control.log).
  • ./mvnw -pl libsql-conformance -am -Dit.test=XerialHarnessTest verify -> 8 tests, 0 failures (/tmp/xerial-parity-i120/lane-9-specific.log, /tmp/xerial-parity-i120/lane-3-specific.log).
  • ./mvnw -pl libsql-jdbc test -> 235 tests, 0 failures (/tmp/xerial-parity-i120/lane-8-unit.log).
  • ./mvnw -pl libsql-conformance -am test -> BUILD SUCCESS (/tmp/xerial-parity-i120/lane-4-module.log).
  • ./mvnw test -> BUILD SUCCESS (/tmp/xerial-parity-i120/lane-10-reactor.log).
  • ./mvnw -pl libsql-jdbc -am package -DskipTests -> BUILD SUCCESS (/tmp/xerial-parity-i120/lane-package.log).
  • ./mvnw spotless:check -> no violations (/tmp/xerial-parity-i120/lane-7-spotless.log).
  • ./tools/export-upstream --verify -> verified (/tmp/xerial-parity-i120/lane-6-verify.log).

Fixes #120. Part of #54.

- Wrap statements and metadata so Statement.getConnection() and
  DatabaseMetaData.getConnection() return the same connection proxy, and
  closing through either releases the namespace once.
- Track the underlying connections per namespace. stop() now fails when a
  namespace still has an open connection, and reports namespaces whose
  cleanup failed instead of clearing them silently. Reconcile an escaped
  raw connection closed through unwrap before declaring a leak.
- Treat SQLite file: memory URIs (file::memory:, ?mode=memory) as
  per-connection namespaces and include the query string in the file
  namespace identity, so options isolate databases instead of being
  ignored.
- Add XerialHarnessTest: wrapped identity, release-once, stop failure on
  a leaked connection, swallowed cleanup failure reporting, file memory
  isolation, and query-string isolation.

No production driver code changed. The JSON1Test and CachedRowSetTest
connection closes stay in the vendored copies: they leak top-level
connections, which the stricter stop() must reject, not connections
obtained through statements or metadata.

Contract: Hrana 3 is the transport and value source; java.sql defines the
connection and wrapper surface; xerial remains the conformance reference.
The query string is namespace identity only because sqld has no URL
pragma surface.

Evidence:
- Red: ./mvnw -pl libsql-conformance -am test-compile failed; the harness
  test could not compile against the old driver/server APIs.
- Green: ./mvnw -pl libsql-conformance -am verify -> 123 tests, 0
  failures, 1 skipped.
- Green: ./mvnw -pl libsql-conformance -am -Pxerial-control verify -> 100
  tests, 0 failures, 1 skipped.
- Green: XerialHarnessTest -> 8 tests, 0 failures.
- Green: ./mvnw -pl libsql-jdbc test -> 235 tests, 0 failures.
- Green: ./mvnw test, ./mvnw -pl libsql-jdbc -am package -DskipTests,
  ./mvnw spotless:check, ./tools/export-upstream --verify.
The full module run interleaves XerialTestServerCleanupTest with the new
harness self-tests. The cleanup tests deliberately leave a namespace
registered after asserting reuse and cleanup failures, and stopForTest
cleared the whole ledger, so the leftover only surfaced when the real
session stop ran before the self-test.

Reset only the namespaces a test creates, and make stopForTest read the
ledger without clearing it. A real leak is no longer masked by a later
test, and the full suite leaves no namespace registered.

Verified with XerialTestServerCleanupTest alone, XerialHarnessTest alone,
and the full conformance verify.
@magnusp
magnusp merged commit 599b65d into main Oct 22, 2026
5 checks passed
@magnusp
magnusp deleted the fix/i120-conformance-proxy branch October 22, 2026 22:49
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.

Conformance proxy lets connections escape and shutdown hides leaked namespaces

1 participant