@pineliner/odb-client 1.5.4 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -2,6 +2,12 @@ import type { BackendType, Connection, DatabaseAdapter, PreparedStatement, Query
|
|
|
2
2
|
import { ODBLiteAdapter } from './odblite'
|
|
3
3
|
import { SocketClient, InteractiveTxConnectionLostError, type SocketTarget } from './socket/client'
|
|
4
4
|
import { runInTransactionContext, ambientTx } from './tx-context'
|
|
5
|
+
|
|
6
|
+
// An ambient-tx JOIN whose target transaction already committed/reaped surfaces as "stream not open" — the
|
|
7
|
+
// write is now inherently standalone, so query/execute fall back to autocommit. Matches the server wording.
|
|
8
|
+
function isStreamClosed(e: any): boolean {
|
|
9
|
+
return /stream .*(not open|already ended|never opened)/i.test(String(e?.message ?? ''))
|
|
10
|
+
}
|
|
5
11
|
import {
|
|
6
12
|
toHrana,
|
|
7
13
|
fromHrana,
|
|
@@ -115,7 +121,13 @@ class OdbliteSocketConnection implements Connection {
|
|
|
115
121
|
const p = typeof sql === 'string' ? params : (sql.args ?? [])
|
|
116
122
|
if (this.streamId != null) return (await this.streamExec(s, p)) as QueryResult<T>
|
|
117
123
|
const amb = ambientTx(this.tenant) // autocommit conn, but inside a tx for my tenant? → join it (see execute)
|
|
118
|
-
if (amb && amb !== this)
|
|
124
|
+
if (amb && amb !== this) {
|
|
125
|
+
try {
|
|
126
|
+
return await amb.query<T>(s, p)
|
|
127
|
+
} catch (e: any) {
|
|
128
|
+
if (!isStreamClosed(e)) throw e // tx we tried to join already closed → run standalone (autocommit) below
|
|
129
|
+
}
|
|
130
|
+
}
|
|
119
131
|
return toQueryResult(await this.one([{ type: 'execute', stmt: { sql: s, args: p.map(toHrana) } }])) as QueryResult<T>
|
|
120
132
|
}
|
|
121
133
|
|
|
@@ -130,7 +142,19 @@ class OdbliteSocketConnection implements Connection {
|
|
|
130
142
|
// fresh autocommit write that would deadlock behind the tx on the writer slot. Same tenant key on both
|
|
131
143
|
// sides (this.tenant), so no auth/database-hash translation for a caller to get wrong.
|
|
132
144
|
const amb = ambientTx(this.tenant)
|
|
133
|
-
if (amb && amb !== this)
|
|
145
|
+
if (amb && amb !== this) {
|
|
146
|
+
try {
|
|
147
|
+
return await amb.execute(s, p)
|
|
148
|
+
} catch (e: any) {
|
|
149
|
+
// GENERIC ROBUSTNESS: the ambient tx we tried to JOIN has already committed/reaped, so its stream is
|
|
150
|
+
// gone ("stream not open"). The write is inherently post-commit now — fall back to a fresh autocommit
|
|
151
|
+
// connection instead of throwing. This makes a non-awaited publish inside a tx degrade gracefully
|
|
152
|
+
// (decoupled) rather than crash, so callers don't strictly need publishAsync to avoid errors — it
|
|
153
|
+
// stays only as the way to express DETERMINISTIC decoupling. Scoped to the JOIN path: a tx's OWN
|
|
154
|
+
// statements still surface the error (we never silently split a real transaction).
|
|
155
|
+
if (!isStreamClosed(e)) throw e
|
|
156
|
+
}
|
|
157
|
+
}
|
|
134
158
|
return toQueryResult(await this.one([{ type: 'execute', stmt: { sql: s, args: p.map(toHrana) } }]))
|
|
135
159
|
}
|
|
136
160
|
|