Repository navigation
Description
Context
sqld runs SQLite in WAL mode. A transaction that reads before it writes holds a snapshot; if another connection commits in between, SQLite refuses the read-to-write upgrade with SQLITE_BUSY_SNAPSHOT (SQLite error: database is locked), and busy_timeout cannot retry it because the snapshot is stale. The application-level fix is to start write transactions with BEGIN IMMEDIATE: take the write lock at BEGIN and let concurrent writers queue instead of failing an upgrade.
LibsqlConnection.setTransactionMode(TransactionMode) already does the right thing (0.2.1): once set, ensureTransactionStarted sends BEGIN IMMEDIATE / BEGIN EXCLUSIVE. But the mode cannot be selected from configuration. LibsqlUrlParser recognises only authToken, busyTimeout, connectTimeout, jwt, password, socketTimeout, ssl and tls, so applications that want the mode have to wrap their DataSource, get at the concrete LibsqlConnection, and call the setter themselves.
A Connection.setReadOnly(true) check is not a substitute. For example, Spring's @Transactional(readOnly = true) goes through Hibernate's setDefaultReadOnly, which does not call Connection.setReadOnly for JPA transactions, so the driver cannot infer a deferred transaction from the JDBC read-only flag. The mode has to be explicit.
Proposal
Accept transactionMode as a JDBC URL query parameter and as a connection property, matching the existing busyTimeout handling:
jdbc:libsql:http://localhost:8080?transactionMode=immediate
- Values:
deferred,immediate,exclusive, case-insensitive, mirroringLibsqlConnection.TransactionMode(and xerial'sSQLiteConfig.TransactionMode). - Default:
deferred, preserving current behaviour. - An unknown value fails at connect with a message that lists the accepted values, rather than silently falling back to
deferred. - Apply it to the connection state in
LibsqlDriver.connect(or carry it throughLibsqlClientConfigif that is the cleaner home). It is per-connection behaviour, not transport configuration.
Acceptance
jdbc:libsql:http://host?transactionMode=immediatemakes the connection's first transaction begin withBEGIN IMMEDIATE.- URL and
Propertiesforms behave the same; the precedence between them is documented. - Parser unit tests cover every accepted value, case insensitivity, the default, and rejection of an unknown value.
- An integration test in
libsql-itestsruns a read-then-write transaction withimmediateand asserts it commits while a second connection writes in between; conformance tests stay green. - The parameter is documented next to the other URL options in the README/JDBC reference.
Downstream
Applications currently work around the missing option by wrapping the DataSource and calling setTransactionMode on each connection. A URL parameter would keep their configuration declarative and remove that wrapper.
Activity
feat(jdbc): accept transactionMode in the JDBC URL and connection pro…
magnusp commentedon Nov 2, 2026
Done in 0.2.2. #295 added
transactionModeas a URL query parameter and aPropertieskey, with case-insensitivedeferred/immediate/exclusive, adeferreddefault, a connect-time error that lists the accepted values, URL-over-Properties precedence, parser tests for every case, and the README parameter table. The tagv0.2.2published the artifact and CI stayed green, including the conformance suite.The read-then-write integration test the acceptance asked for landed separately in #297: it reads inside an
immediatetransaction, proves a second connection's insert waits, writes and commits in the first transaction, and checks both rows.The auth-refusal transaction-state reset found while testing the driver is follow-up #296, not part of this issue.