Skip to content

fix(jdbc): match xerial transaction begin and re-begin lifecycle - #64

Merged
magnusp merged 1 commit into
mainfrom
fix/40-transaction-lifecycle
Oct 21, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/40-transaction-lifecycle

Conversation

@magnusp

@magnusp magnusp commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Closes #40. Part of #54.

What changed

  • setAutoCommit(false) now begins a transaction immediately in the configured mode instead of waiting for the first statement.
  • Added LibsqlConnection.TransactionMode (DEFERRED, IMMEDIATE, EXCLUSIVE) with setTransactionMode(...)/getTransactionMode(). The default is DEFERRED, matching xerial 3.53.4.0.
  • commit() and rollback() now send the end command and the next BEGIN in one pipeline request, so a non-auto-commit connection always has the next transaction open.
  • setAutoCommit(true) commits the open transaction and closes the Hrana stream without starting another transaction.
  • Kept the rollback() swallows failures and reports success #30 behavior: a failed COMMIT/ROLLBACK still throws and keeps the transaction stream for a retry. Nothing here depends on rollback swallowing errors.
  • Documented the begin timing, mode, re-begin, and lock behavior on setAutoCommit.
  • Added unit tests for eager begin, configured mode, commit/re-begin, rollback/re-begin, and re-enabling auto-commit.
  • Added TransactionLifecycleIT concurrency tests against sqld for the documented lock behavior.

Contract decision

Parity with xerial 3.53.4.0 SQLiteConnection.setAutoCommit/commit/rollback:

  • disabling auto-commit begins eagerly in the configured transaction mode;
  • default mode is DEFERRED;
  • commit()/rollback() end the current transaction and immediately begin the next one;
  • re-enabling auto-commit commits and ends the transaction stream;
  • an idle DEFERRED transaction holds no lock, while IMMEDIATE/EXCLUSIVE acquire the write lock at begin.

This replaces the driver's previous read-write BEGIN IMMEDIATE default with xerial's DEFERRED default. Callers that want the old lock behavior can select IMMEDIATE through setTransactionMode.

Evidence

Failing before the fix (old LibsqlConnection, new unit tests):

$ ./mvnw -q -pl libsql-jdbc -am -Dtest=LibsqlConnectionTest -Dsurefire.failIfNoSpecifiedTests=false test
Tests run: 8, Failures: 4, Errors: 0, Skipped: 0
  commitBeginsNextTransactionOnTheSameStream:53
  Expecting actual: [] to contain exactly: ["BEGIN", "COMMIT", "BEGIN"]
  disablingAutoCommitBeginsTransactionImmediately:41
  Expecting actual: [] to contain exactly: ["BEGIN"]
  enablingAutoCommitCommitsAndClosesTheOpenTransaction:79
  Expecting actual: [] to contain exactly: ["BEGIN", "COMMIT"]
  rollbackBeginsNextTransactionOnTheSameStream:66
  Expecting actual: [] to contain exactly: ["BEGIN", "ROLLBACK", "BEGIN"]

Passing after the fix:

$ ./mvnw -pl libsql-jdbc -am test
Tests run: 68, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

$ DOCKER_CONFIG=/tmp/docker-config ./mvnw -pl libsql-itests -am verify
TransactionLifecycleIT   Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
libSQL Integration Tests Tests run: 18, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

$ ./mvnw -pl libsql-conformance -am verify
Tests run: 106, Failures: 0, Errors: 0, Skipped: 1
BUILD SUCCESS

$ ./mvnw spotless:apply
BUILD SUCCESS

DOCKER_CONFIG=/tmp/docker-config uses an empty public-image auth file because the sandbox blocks ~/.docker/config.json (AGENTS.md section 4). It does not change test behavior.

Deviations

  • Configuration surface: this lane owns only LibsqlConnection.java, so the mode is exposed as LibsqlConnection.setTransactionMode(...) rather than as a JDBC URL property / LibsqlClientConfig field. Begin timing, default mode, and lock behavior match xerial; URL-based configuration of the mode would require changes in the URL parser and client config.
  • Transport detail: COMMIT/ROLLBACK and the following BEGIN are sent as two execute steps in one Hrana pipeline request. This produces the same observable state as xerial's two calls while avoiding an extra round trip. The stream is closed only when auto-commit is re-enabled, as required to keep the next transaction open.

Out-of-scope defects

  • IMPLEMENTATION_BUGS_AND_RESOLUTIONS.md still describes BEGIN IMMEDIATE as the read-write default. It is now stale after this parity change, but documentation files are outside this lane's owned files.

@magnusp
magnusp force-pushed the fix/40-transaction-lifecycle branch from fb7260f to ebefc87 Compare October 21, 2026 22:12
Closes #40. Part of #54.

setAutoCommit(false) now begins eagerly in the configured transaction mode (DEFERRED by default), and commit/rollback begin the next transaction in the same pipeline. Re-enabling auto-commit commits and closes the Hrana stream.
@magnusp
magnusp force-pushed the fix/40-transaction-lifecycle branch from ebefc87 to 7ec6a5d Compare October 21, 2026 22:15
@magnusp
magnusp merged commit de56052 into main Oct 21, 2026
5 checks passed
@magnusp
magnusp deleted the fix/40-transaction-lifecycle branch October 21, 2026 22:19
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.

Transaction begin mode and commit/rollback lifecycle differ from sqlite-jdbc

1 participant