Skip to content

fix(jdbc): read updateCount under the statement lock - #235

Merged
magnusp merged 1 commit into
mainfrom
fix/i197-update-count-lock
Oct 24, 2026
Merged

magnusp merged 1 commit into
mainfrom
fix/i197-update-count-lock

Conversation

@magnusp

@magnusp magnusp commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Fixes #197

executeUpdate returned updateCount with a plain field read, outside stateLock. Every write to that field happens inside a synchronized (stateLock) block, but the read after execute had no happens-before edge, so a concurrent close() or execute could hand back a stale count.

Change

  • Add LibsqlStatement.snapshotUpdateCount(), which reads updateCount under stateLock.
  • Use it in LibsqlStatement.executeUpdate(String) and LibsqlPreparedStatement.executeUpdate().

This is a correctness fix, not a refactor. getUpdateCount() already read the field under stateLock; the two read paths now share the same lock discipline, so executeUpdate and getUpdateCount cannot observe different values. There is no checkClosed() in the new method: post-close executeUpdate keeps working, and the prepared statement already threw during execute.

On the race

The race is hard to reproduce without instrumentation: the stale read is a memory-visibility gap, not a deterministic interleaving, and the field is a long written under the lock. A test that tries to force it would be timing-dependent and flaky, so this PR does not add one. The argument is code-level:

  • All writes (attachResult, discardCurrentResult, beginExecution, getMoreResults, executeBatch) are inside synchronized (stateLock).
  • snapshotUpdateCount() takes the same monitor before reading, which gives the read a happens-before edge to the last write and prevents a torn or stale long.
  • getUpdateCount() at LibsqlStatement:398 already did exactly this; the fix makes the two paths consistent.

No messages, post-close behavior, or update-count values change.

Verification

Lane Command Result
Unit ./mvnw test 405 in libsql-jdbc (incl. LibsqlStatementConcurrencyTest, 7/7) + 35 codec, 0 failures
Spotless ./mvnw spotless:check clean
Conformance ./mvnw -pl libsql-conformance -am verify 272 run, 0 failures, 7 skipped (pre-existing)
Integration DOCKER_CONFIG=... ./mvnw -pl libsql-itests -am verify 43 run, 0 failures

@magnusp
magnusp merged commit 2e05b72 into main Oct 24, 2026
6 checks passed
@magnusp
magnusp deleted the fix/i197-update-count-lock branch October 24, 2026 18:37
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.

executeUpdate reads updateCount outside stateLock

1 participant