Skip to content

Commit 7f13ccc

Browse files
committed
docs: retire IMPLEMENTATION_BUGS_AND_RESOLUTIONS.md
1 parent 2d2ca50 commit 7f13ccc

7 files changed

Lines changed: 54 additions & 265 deletions

File tree

‎IMPLEMENTATION_BUGS_AND_RESOLUTIONS.md‎

Lines changed: 0 additions & 259 deletions
This file was deleted.

‎libsql-client/src/main/java/com/github/magnusp/libsql/client/LibsqlHttpClient.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private static HttpClient getOrCreateHttpClient(Duration connectTimeout) {
6060
}
6161

6262
public StmtResult executeOneShot(Stmt stmt) {
63-
// Mitigation for Bug 1: pipeline [execute, close] in a single roundtrip to prevent 128-stream 10s stall
63+
// One-shot: [execute, close] in one round trip, so no idle stream is left for the server to reap.
6464
PipelineReqBody req = new PipelineReqBody(
6565
null,
6666
List.of(

‎libsql-conformance/README.md‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,51 @@ namespaces are not retained.
3535
The conformance copy of an upstream test diverges from xerial where Hrana connection
3636
lifecycle makes the original behavior impossible; each divergence is recorded in
3737
`../upstream-xerial_sqlite-jdbc.manifest`.
38+
39+
## Divergences from a local SQLite connection
40+
41+
Hrana over HTTP has no connection. A stream is identified only by the `baton`
42+
in the pipeline request: `baton: null` creates a new stream, and the server
43+
closes streams after a short period of inactivity because there is no
44+
connection that would let it detect an abandoned stream
45+
(`../vendor/HRANA_3_SPEC.md`, "Hrana over HTTP"). Two observable divergences
46+
from xerial `sqlite-jdbc`, which owns one SQLite connection per JDBC
47+
connection, follow from that model. Per-test divergences in the vendored
48+
suite are recorded separately in `../upstream-xerial_sqlite-jdbc.manifest`.
49+
50+
### Auto-commit holds no stream
51+
52+
An auto-commit statement runs as a one-shot pipeline that executes the
53+
statement and closes the stream in the same round trip. The driver keeps no
54+
session stream open between statements, so an idle connection consumes no
55+
server stream slot and holds no baton for the idle expiry to invalidate.
56+
Keeping one open only to preserve session state would reintroduce
57+
stale-baton failures on idle connections.
58+
59+
Because `StmtResult.last_insert_rowid` describes the insert on one stream
60+
and nothing else, a later `SELECT last_insert_rowid()` runs on a fresh
61+
stream and returns `0`, even when the same `Statement` inserted a row
62+
immediately before. xerial returns that insert's rowid instead. Use
63+
`Statement.getGeneratedKeys()` after the insert, which the driver builds
64+
from `StmtResult.last_insert_rowid` with no extra round trip, or run the
65+
insert and the query inside one explicit transaction so both run on the
66+
same stream. `LibsqlGeneratedKeysTest` pins the generated-keys path.
67+
68+
### Transactions begin lazily
69+
70+
`setAutoCommit(false)` sends nothing. The next statement begins the
71+
transaction on a new stream with `BEGIN`, `BEGIN IMMEDIATE`, or
72+
`BEGIN EXCLUSIVE` according to
73+
`LibsqlConnection.setTransactionMode(TransactionMode)` (default `DEFERRED`),
74+
sent as its own pipeline so a failed begin cannot be followed by the
75+
statement running outside a transaction. `commit()` and `rollback()` close
76+
the stream and do not begin the next transaction, so an idle non-auto-commit
77+
connection likewise holds no stream.
78+
79+
The divergence is timing only: an `IMMEDIATE` or `EXCLUSIVE` transaction
80+
takes its write lock when the first statement begins it, not at
81+
`setAutoCommit(false)`. Select that mode when the lock must be held from
82+
transaction start; with the default `DEFERRED`, two readers that both
83+
upgrade can still see `SQLITE_BUSY`, as in SQLite. A transaction left open
84+
and idle longer than the server timeout can still fail on a stale baton,
85+
so keep transactions short and retry the work in a fresh one.

‎libsql-itests/src/test/java/com/github/magnusp/libsql/itest/LibsqlHttpClientIT.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void testNonInteractiveOneShotSelect() {
3939

4040
@Test
4141
void testAutocommitWriteBurstDoesNotStall() {
42-
// Verifies mitigation for Bug 1: 128-stream 10-second stall
42+
// One-shot close must keep a write burst from exhausting the server stream pool.
4343
LibsqlClientConfig config = new LibsqlClientConfig(getHttpUrl(), null);
4444
LibsqlHttpClient client = new LibsqlHttpClient(config);
4545

@@ -63,7 +63,7 @@ void testInteractiveTransactionLifecycleWithBaton() {
6363
LibsqlClientConfig config = new LibsqlClientConfig(getHttpUrl(), null);
6464
LibsqlHttpClient client = new LibsqlHttpClient(config);
6565

66-
// 1. BEGIN IMMEDIATE (prevents Bug 2 lock upgrade deadlocks)
66+
// 1. BEGIN IMMEDIATE takes the write lock at begin, avoiding the deferred upgrade.
6767
PipelineReqBody beginReq = new PipelineReqBody(null, List.of(new StreamRequest.Execute(new Stmt("BEGIN IMMEDIATE"))));
6868
PipelineRespBody beginResp = client.sendPipeline(beginReq, null);
6969

‎libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlConnection.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public StmtResult executeStatement(Stmt stmt) throws SQLException {
129129
checkClosed();
130130
try {
131131
if (autoCommit) {
132-
// One-shot execution with single-roundtrip close (prevents Bug 1 128-stream 10s stall)
132+
// One-shot: single-round-trip close, so no idle stream is left for the server to reap.
133133
return client.executeOneShot(stmt);
134134
} else {
135135
ensureTransactionStarted();

‎libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlPreparedStatement.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public class LibsqlPreparedStatement extends LibsqlStatement implements Prepared
5050
private final String sql;
5151
private final boolean returnsResultSet;
5252
private final int parameterCount;
53-
// Mitigation for Bug 7: Sequential ArrayList instead of Map to eliminate Integer autoboxing overhead
53+
// Sequential list indexed by JDBC parameter position; no map or boxing overhead.
5454
private final List<Value> parameters = new ArrayList<>();
5555

5656
public LibsqlPreparedStatement(LibsqlConnection connection, String sql) {

‎libsql-jdbc/src/main/java/com/github/magnusp/libsql/jdbc/LibsqlResultSet.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class LibsqlResultSet implements ResultSet {
7474
private int fetchSize = 0;
7575
private LibsqlResultSetMetaData metadata;
7676

77-
// Mitigation for Bug 6: O(1) column label lookup
77+
// O(1) column label lookup; the map is built lazily on first use.
7878
private Map<String, Integer> columnNameToIndex;
7979

8080
public LibsqlResultSet(Statement statement, StmtResult result) {

0 commit comments

Comments
 (0)