@pineliner/odb-client 1.2.0 → 1.5.0
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/dist/database/adapters/odblite-socket.d.ts +14 -1
- package/dist/database/adapters/odblite-socket.d.ts.map +1 -1
- package/dist/database/adapters/socket/client.d.ts +18 -1
- package/dist/database/adapters/socket/client.d.ts.map +1 -1
- package/dist/database/adapters/socket/protocol.d.ts +3 -0
- package/dist/database/adapters/socket/protocol.d.ts.map +1 -1
- package/dist/index.cjs +176 -51
- package/dist/index.js +176 -51
- package/package.json +1 -1
- package/src/database/adapters/odblite-socket.ts +118 -38
- package/src/database/adapters/socket/client.ts +111 -13
- package/src/database/adapters/socket/protocol.ts +15 -0
|
@@ -10,6 +10,10 @@ export declare class OdbliteSocketAdapter implements DatabaseAdapter {
|
|
|
10
10
|
disconnect(): Promise<void>;
|
|
11
11
|
isHealthy(): Promise<boolean>;
|
|
12
12
|
}
|
|
13
|
+
/** Socket wire-protocol version this client speaks. The connect-time handshake (isHealthy → get_version)
|
|
14
|
+
* requires the server to match; on mismatch the auto-adapter falls back to HTTP. Keep in lockstep with
|
|
15
|
+
* router_v2's SOCKET_PROTOCOL. */
|
|
16
|
+
export declare const CLIENT_SOCKET_PROTOCOL = 2;
|
|
13
17
|
/** Default same-host unix socket (free — v1 only uses /tmp/odblite-internal.sock). */
|
|
14
18
|
export declare const DEFAULT_UNIX_SOCKET = "/tmp/odblite.sock";
|
|
15
19
|
/**
|
|
@@ -36,12 +40,21 @@ export declare function parseSocketTarget(s?: string): SocketTarget;
|
|
|
36
40
|
*/
|
|
37
41
|
export declare class OdbliteAutoAdapter implements DatabaseAdapter {
|
|
38
42
|
readonly type: BackendType;
|
|
39
|
-
private readonly
|
|
43
|
+
private readonly opts;
|
|
44
|
+
private chosen;
|
|
45
|
+
private onHttp;
|
|
46
|
+
private nextRetryAt;
|
|
40
47
|
constructor(opts: {
|
|
41
48
|
socket?: OdbliteSocketConfig;
|
|
42
49
|
http: any;
|
|
43
50
|
});
|
|
44
51
|
private pick;
|
|
52
|
+
/** Probe the cascade once; return a live socket adapter or null. */
|
|
53
|
+
private trySocket;
|
|
54
|
+
/** When we fell back to HTTP, re-probe the socket on a cooldown so a socket that
|
|
55
|
+
* comes up LATER (server started after the app) is picked up without a restart —
|
|
56
|
+
* subsequent connections then use it. */
|
|
57
|
+
private maybeUpgrade;
|
|
45
58
|
/** Ordered socket targets to probe. Explicit override → a single target; else the
|
|
46
59
|
* zero-config cascade unix (same host) → TCP (host derived from ODB_LITE_URL). */
|
|
47
60
|
private candidates;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"odblite-socket.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite-socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAkC,MAAM,UAAU,CAAA;AAExG,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"odblite-socket.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite-socket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAkC,MAAM,UAAU,CAAA;AAExG,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAA;AA0KjE,MAAM,WAAW,mBAAoB,SAAQ,YAAY;CAAG;AAE5D,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,MAAM,EAAE,mBAAmB;IAIjC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAMzC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAmBpC;AAED;;mCAEmC;AACnC,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC,sFAAsF;AACtF,eAAO,MAAM,mBAAmB,sBAAsB,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAO1D;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6C;IAClE,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAAI;gBAEX,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE;YAK/C,IAAI;IAWlB,oEAAoE;YACtD,SAAS;IAgBvB;;8CAE0C;YAC5B,YAAY;IAY1B;uFACmF;IACnF,OAAO,CAAC,UAAU;IAgBZ,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAIzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAGpC"}
|
|
@@ -6,6 +6,11 @@ import { type StreamRequest, type StreamResult } from './protocol';
|
|
|
6
6
|
*
|
|
7
7
|
* Bun-only (uses `Bun.connect`). app-router runs on Bun; the HTTP `odblite`
|
|
8
8
|
* backend remains the universal fallback for non-Bun environments.
|
|
9
|
+
*
|
|
10
|
+
* Liveness invariant: an in-flight request must ALWAYS terminate. If the
|
|
11
|
+
* connection drops (close/error) or a reply never arrives, the pending promise
|
|
12
|
+
* is REJECTED — never left hanging. A hung pending request would wedge the
|
|
13
|
+
* caller's transaction, hold its write lane, and cascade into a 30s timeout.
|
|
9
14
|
*/
|
|
10
15
|
export interface SocketTarget {
|
|
11
16
|
unix?: string;
|
|
@@ -19,10 +24,22 @@ export declare class SocketClient {
|
|
|
19
24
|
private writer;
|
|
20
25
|
private readonly pending;
|
|
21
26
|
private nextId;
|
|
27
|
+
private nextStreamId;
|
|
22
28
|
private ready;
|
|
23
29
|
constructor(target: SocketTarget);
|
|
30
|
+
/** Reject + clear every in-flight request — called on disconnect/error/close so a
|
|
31
|
+
* dropped connection FAILS its requests (the caller retries) instead of hanging. */
|
|
32
|
+
private failPending;
|
|
24
33
|
private connect;
|
|
25
|
-
|
|
34
|
+
/** Reconnect with bounded backoff — bridges a router restart so a request landing in the down-window
|
|
35
|
+
* waits it out instead of failing. After the last attempt the error propagates (caller retries / the
|
|
36
|
+
* auto-adapter can fall back to HTTP). Backoff: 50·100·200·400·800·1600ms ≈ 3s total by default. */
|
|
37
|
+
private connectWithRetry;
|
|
38
|
+
/** Allocate a fresh stream id for an interactive transaction (unique per connection). The server
|
|
39
|
+
* pins one SQLite connection per stream id, so all of a tx's statements run on it (read-your-own-
|
|
40
|
+
* writes). The id is carried in every frame of that tx via pipeline()'s `stream` arg. */
|
|
41
|
+
allocStream(): number;
|
|
42
|
+
pipeline(tenant: string, requests: StreamRequest[], stream?: number): Promise<StreamResult[]>;
|
|
26
43
|
close(): void;
|
|
27
44
|
}
|
|
28
45
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5F
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAE5F;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAqBD,qBAAa,YAAY;IASX,OAAO,CAAC,QAAQ,CAAC,MAAM;IARnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,KAAK,CAAe;gBAEC,MAAM,EAAE,YAAY;IAOjD;yFACqF;IACrF,OAAO,CAAC,WAAW;YAQL,OAAO;IAsDrB;;yGAEqG;YACvF,gBAAgB;IAc9B;;8FAE0F;IAC1F,WAAW,IAAI,MAAM;IAIf,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA0BnG,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -95,6 +95,8 @@ export type StreamRequest = {
|
|
|
95
95
|
sql: string;
|
|
96
96
|
} | {
|
|
97
97
|
type: 'get_autocommit';
|
|
98
|
+
} | {
|
|
99
|
+
type: 'get_version';
|
|
98
100
|
} | {
|
|
99
101
|
type: 'close';
|
|
100
102
|
};
|
|
@@ -111,6 +113,7 @@ export interface AtomicStep {
|
|
|
111
113
|
expect?: 'one' | 'atLeastOne' | number;
|
|
112
114
|
}
|
|
113
115
|
export declare function toBatch(steps: AtomicStep[]): Batch;
|
|
116
|
+
export declare function mightReturnRows(sql: string): boolean;
|
|
114
117
|
interface Writable {
|
|
115
118
|
write(data: Uint8Array): number;
|
|
116
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/protocol.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../src/database/adapters/socket/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpC,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,GAAG,UAAU,CAQ1C;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,GAAG,CAe5C;AAGD,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,UAAU,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,EAAE,CAAA;IAClD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AACD,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAA;AAC7B,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAC5B,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,KAAK,GAAG,YAAY,GAAG,MAAM,CAAA;KAAE,CAAA;CACrD;AACD,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,EAAE,CAAA;CACnB;AACD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAA;IAC/B,IAAI,EAAE,UAAU,EAAE,EAAE,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AACD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAA;IACnC,WAAW,EAAE,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,CAAA;CAC3D;AACD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AACD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AACrB,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAA;AAGxC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,MAAM,CAAC,EAAE,KAAK,GAAG,YAAY,GAAG,MAAM,CAAA;CACvC;AACD,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,KAAK,CAQlD;AAOD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,UAAU,QAAQ;IAChB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAA;CAChC;AACD,qBAAa,WAAW;IAEV,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,OAAO,CAAC,OAAO,CAA0B;gBACZ,MAAM,EAAE,QAAQ;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO3B,KAAK,IAAI,IAAI;CAOd;AAKD,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAA0B;IACrC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAY9B"}
|
package/dist/index.cjs
CHANGED
|
@@ -2204,6 +2204,10 @@ var __webpack_exports__ = {};
|
|
|
2204
2204
|
}))
|
|
2205
2205
|
};
|
|
2206
2206
|
}
|
|
2207
|
+
const READ_ONLY_RE = /^\s*(?:SELECT|EXPLAIN|VALUES)\b/i;
|
|
2208
|
+
function mightReturnRows(sql) {
|
|
2209
|
+
return READ_ONLY_RE.test(sql) || /\bRETURNING\b/i.test(sql);
|
|
2210
|
+
}
|
|
2207
2211
|
class FrameWriter {
|
|
2208
2212
|
socket;
|
|
2209
2213
|
pending = Buffer.alloc(0);
|
|
@@ -2231,6 +2235,7 @@ var __webpack_exports__ = {};
|
|
|
2231
2235
|
}
|
|
2232
2236
|
}
|
|
2233
2237
|
}
|
|
2238
|
+
const MAX_FRAME = 1024 * (Number(globalThis.process?.env?.ODB_MAX_FRAME_MB) || 64) * 1024;
|
|
2234
2239
|
class FrameReader {
|
|
2235
2240
|
buf = Buffer.alloc(0);
|
|
2236
2241
|
push(chunk) {
|
|
@@ -2241,6 +2246,7 @@ var __webpack_exports__ = {};
|
|
|
2241
2246
|
const out = [];
|
|
2242
2247
|
while(this.buf.length >= 4){
|
|
2243
2248
|
const len = this.buf.readUInt32LE(0);
|
|
2249
|
+
if (len > MAX_FRAME) throw new Error(`odblite reply frame too large: ${len} bytes (max ${MAX_FRAME})`);
|
|
2244
2250
|
if (this.buf.length < 4 + len) break;
|
|
2245
2251
|
out.push(this.buf.subarray(4, 4 + len));
|
|
2246
2252
|
this.buf = this.buf.subarray(4 + len);
|
|
@@ -2248,6 +2254,10 @@ var __webpack_exports__ = {};
|
|
|
2248
2254
|
return out;
|
|
2249
2255
|
}
|
|
2250
2256
|
}
|
|
2257
|
+
const REQUEST_TIMEOUT_MS = Number(globalThis.process?.env?.ODB_SOCKET_REQUEST_TIMEOUT_MS) || 30000;
|
|
2258
|
+
const RECONNECT_ATTEMPTS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_ATTEMPTS) || 6;
|
|
2259
|
+
const RECONNECT_BASE_MS = Number(globalThis.process?.env?.ODB_SOCKET_RECONNECT_BASE_MS) || 50;
|
|
2260
|
+
const sleep = (ms)=>new Promise((r)=>setTimeout(r, ms));
|
|
2251
2261
|
class SocketClient {
|
|
2252
2262
|
target;
|
|
2253
2263
|
socket;
|
|
@@ -2255,12 +2265,20 @@ var __webpack_exports__ = {};
|
|
|
2255
2265
|
writer;
|
|
2256
2266
|
pending = new Map();
|
|
2257
2267
|
nextId = 1;
|
|
2268
|
+
nextStreamId = 1;
|
|
2258
2269
|
ready;
|
|
2259
2270
|
constructor(target){
|
|
2260
2271
|
this.target = target;
|
|
2261
2272
|
this.ready = this.connect();
|
|
2262
2273
|
this.ready.catch(()=>{});
|
|
2263
2274
|
}
|
|
2275
|
+
failPending(err) {
|
|
2276
|
+
for (const p of this.pending.values()){
|
|
2277
|
+
clearTimeout(p.timer);
|
|
2278
|
+
p.reject(err);
|
|
2279
|
+
}
|
|
2280
|
+
this.pending.clear();
|
|
2281
|
+
}
|
|
2264
2282
|
async connect() {
|
|
2265
2283
|
const Bun = globalThis.Bun;
|
|
2266
2284
|
if (!Bun?.connect) throw new Error('odblite-socket backend requires Bun (Bun.connect)');
|
|
@@ -2273,43 +2291,93 @@ var __webpack_exports__ = {};
|
|
|
2273
2291
|
this.socket = await Bun.connect({
|
|
2274
2292
|
...opts,
|
|
2275
2293
|
socket: {
|
|
2276
|
-
data: (
|
|
2277
|
-
|
|
2294
|
+
data: (s, chunk)=>{
|
|
2295
|
+
let frames;
|
|
2296
|
+
try {
|
|
2297
|
+
frames = this.reader.push(chunk);
|
|
2298
|
+
} catch (err) {
|
|
2299
|
+
this.socket = void 0;
|
|
2300
|
+
this.reader = new FrameReader();
|
|
2301
|
+
try {
|
|
2302
|
+
s.end?.();
|
|
2303
|
+
} catch {}
|
|
2304
|
+
this.failPending(err instanceof Error ? err : new Error(`odblite framing error: ${err}`));
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
for (const frame of frames){
|
|
2278
2308
|
const msg = JSON.parse(frame.toString('utf8'));
|
|
2279
|
-
const
|
|
2280
|
-
if (
|
|
2309
|
+
const p = this.pending.get(msg.id);
|
|
2310
|
+
if (p) {
|
|
2281
2311
|
this.pending.delete(msg.id);
|
|
2282
|
-
|
|
2312
|
+
clearTimeout(p.timer);
|
|
2313
|
+
p.resolve(msg.results);
|
|
2283
2314
|
}
|
|
2284
2315
|
}
|
|
2285
2316
|
},
|
|
2286
2317
|
drain: (_s)=>this.writer.flush(),
|
|
2287
2318
|
close: ()=>{
|
|
2288
2319
|
this.socket = void 0;
|
|
2320
|
+
this.reader = new FrameReader();
|
|
2321
|
+
this.failPending(new Error('odblite socket disconnected — request not completed'));
|
|
2322
|
+
},
|
|
2323
|
+
error: (_s, err)=>{
|
|
2324
|
+
this.socket = void 0;
|
|
2325
|
+
this.reader = new FrameReader();
|
|
2326
|
+
this.failPending(err instanceof Error ? err : new Error(`odblite socket error: ${err}`));
|
|
2289
2327
|
}
|
|
2290
2328
|
}
|
|
2291
2329
|
});
|
|
2292
2330
|
this.writer = new FrameWriter(this.socket);
|
|
2293
2331
|
}
|
|
2294
|
-
async
|
|
2332
|
+
async connectWithRetry() {
|
|
2333
|
+
let lastErr;
|
|
2334
|
+
for(let attempt = 0; attempt < RECONNECT_ATTEMPTS; attempt++)try {
|
|
2335
|
+
await this.connect();
|
|
2336
|
+
return;
|
|
2337
|
+
} catch (err) {
|
|
2338
|
+
lastErr = err;
|
|
2339
|
+
if (attempt < RECONNECT_ATTEMPTS - 1) await sleep(RECONNECT_BASE_MS * 2 ** attempt);
|
|
2340
|
+
}
|
|
2341
|
+
throw lastErr instanceof Error ? lastErr : new Error(`odblite socket reconnect failed: ${lastErr}`);
|
|
2342
|
+
}
|
|
2343
|
+
allocStream() {
|
|
2344
|
+
return this.nextStreamId++;
|
|
2345
|
+
}
|
|
2346
|
+
async pipeline(tenant, requests, stream) {
|
|
2347
|
+
await this.ready.catch(()=>{});
|
|
2295
2348
|
if (!this.socket) {
|
|
2296
|
-
this.ready = this.
|
|
2349
|
+
this.ready = this.connectWithRetry();
|
|
2297
2350
|
this.ready.catch(()=>{});
|
|
2351
|
+
await this.ready;
|
|
2298
2352
|
}
|
|
2299
|
-
await this.ready;
|
|
2300
2353
|
const id = this.nextId++;
|
|
2301
|
-
return new Promise((resolve)=>{
|
|
2302
|
-
|
|
2303
|
-
|
|
2354
|
+
return new Promise((resolve, reject)=>{
|
|
2355
|
+
const timer = setTimeout(()=>{
|
|
2356
|
+
if (this.pending.delete(id)) reject(new Error(`odblite socket request ${id} timed out after ${REQUEST_TIMEOUT_MS}ms`));
|
|
2357
|
+
}, REQUEST_TIMEOUT_MS);
|
|
2358
|
+
timer.unref?.();
|
|
2359
|
+
this.pending.set(id, {
|
|
2360
|
+
resolve,
|
|
2361
|
+
reject,
|
|
2362
|
+
timer
|
|
2363
|
+
});
|
|
2364
|
+
const payload = null != stream ? {
|
|
2304
2365
|
id,
|
|
2305
2366
|
tenant,
|
|
2367
|
+
stream,
|
|
2306
2368
|
requests
|
|
2307
|
-
}
|
|
2369
|
+
} : {
|
|
2370
|
+
id,
|
|
2371
|
+
tenant,
|
|
2372
|
+
requests
|
|
2373
|
+
};
|
|
2374
|
+
this.writer.send(Buffer.from(JSON.stringify(payload), 'utf8'));
|
|
2308
2375
|
});
|
|
2309
2376
|
}
|
|
2310
2377
|
close() {
|
|
2311
2378
|
this.socket?.end?.();
|
|
2312
2379
|
this.socket = void 0;
|
|
2380
|
+
this.failPending(new Error('odblite socket closed'));
|
|
2313
2381
|
}
|
|
2314
2382
|
}
|
|
2315
2383
|
function toQueryResult(res) {
|
|
@@ -2333,21 +2401,22 @@ var __webpack_exports__ = {};
|
|
|
2333
2401
|
class OdbliteSocketConnection {
|
|
2334
2402
|
client;
|
|
2335
2403
|
tenant;
|
|
2404
|
+
streamId;
|
|
2336
2405
|
databaseName;
|
|
2337
2406
|
databaseHash;
|
|
2338
2407
|
sql;
|
|
2339
|
-
|
|
2340
|
-
constructor(client, tenant,
|
|
2408
|
+
beginPending = false;
|
|
2409
|
+
constructor(client, tenant, streamId){
|
|
2341
2410
|
this.client = client;
|
|
2342
2411
|
this.tenant = tenant;
|
|
2412
|
+
this.streamId = streamId;
|
|
2343
2413
|
this.databaseName = tenant;
|
|
2344
2414
|
this.databaseHash = tenant;
|
|
2345
|
-
this.buffer = buffer;
|
|
2346
2415
|
const self = this;
|
|
2347
2416
|
this.sql = function(strings, ...values) {
|
|
2348
2417
|
if (strings && 'object' == typeof strings && 'raw' in strings) {
|
|
2349
2418
|
const text = strings.reduce((a, s, i)=>a + s + (i < values.length ? '?' : ''), '');
|
|
2350
|
-
const returns =
|
|
2419
|
+
const returns = mightReturnRows(text);
|
|
2351
2420
|
return (returns ? self.query(text, values) : self.execute(text, values)).then((r)=>r.rows);
|
|
2352
2421
|
}
|
|
2353
2422
|
throw new Error('sql() fragment helper not implemented in odblite-socket');
|
|
@@ -2358,7 +2427,32 @@ var __webpack_exports__ = {};
|
|
|
2358
2427
|
if (!res) throw new Error('odblite-socket: empty pipeline response');
|
|
2359
2428
|
return res;
|
|
2360
2429
|
}
|
|
2430
|
+
async streamExec(sql, params = []) {
|
|
2431
|
+
const reqs = [];
|
|
2432
|
+
if (this.beginPending) {
|
|
2433
|
+
reqs.push({
|
|
2434
|
+
type: 'execute',
|
|
2435
|
+
stmt: {
|
|
2436
|
+
sql: 'BEGIN'
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
this.beginPending = false;
|
|
2440
|
+
}
|
|
2441
|
+
reqs.push({
|
|
2442
|
+
type: 'execute',
|
|
2443
|
+
stmt: {
|
|
2444
|
+
sql,
|
|
2445
|
+
args: params.map(toHrana)
|
|
2446
|
+
}
|
|
2447
|
+
});
|
|
2448
|
+
const results = await this.client.pipeline(this.tenant, reqs, this.streamId);
|
|
2449
|
+
if (reqs.length > 1 && results[0]?.type === 'error') throw new Error(results[0].error.message);
|
|
2450
|
+
const res = results[results.length - 1];
|
|
2451
|
+
if (!res) throw new Error('odblite-socket: empty stream response');
|
|
2452
|
+
return toQueryResult(res);
|
|
2453
|
+
}
|
|
2361
2454
|
async query(sql, params = []) {
|
|
2455
|
+
if (null != this.streamId) return await this.streamExec(sql, params);
|
|
2362
2456
|
return toQueryResult(await this.one([
|
|
2363
2457
|
{
|
|
2364
2458
|
type: 'execute',
|
|
@@ -2372,16 +2466,7 @@ var __webpack_exports__ = {};
|
|
|
2372
2466
|
async execute(sql, params = []) {
|
|
2373
2467
|
const s = 'string' == typeof sql ? sql : sql.sql;
|
|
2374
2468
|
const p = 'string' == typeof sql ? params : sql.args ?? [];
|
|
2375
|
-
if (this.
|
|
2376
|
-
this.buffer.push({
|
|
2377
|
-
sql: s,
|
|
2378
|
-
params: p
|
|
2379
|
-
});
|
|
2380
|
-
return {
|
|
2381
|
-
rows: [],
|
|
2382
|
-
rowsAffected: 0
|
|
2383
|
-
};
|
|
2384
|
-
}
|
|
2469
|
+
if (null != this.streamId) return this.streamExec(s, p);
|
|
2385
2470
|
return toQueryResult(await this.one([
|
|
2386
2471
|
{
|
|
2387
2472
|
type: 'execute',
|
|
@@ -2410,24 +2495,33 @@ var __webpack_exports__ = {};
|
|
|
2410
2495
|
};
|
|
2411
2496
|
}
|
|
2412
2497
|
async transaction(fn) {
|
|
2413
|
-
if (this.
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
const
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2498
|
+
if (null != this.streamId) return fn(this);
|
|
2499
|
+
const tx = new OdbliteSocketConnection(this.client, this.tenant, this.client.allocStream());
|
|
2500
|
+
tx.beginPending = true;
|
|
2501
|
+
try {
|
|
2502
|
+
const out = await fn(tx);
|
|
2503
|
+
if (!tx.beginPending) await tx.streamExec('COMMIT');
|
|
2504
|
+
return out;
|
|
2505
|
+
} catch (e) {
|
|
2506
|
+
if (!tx.beginPending) await tx.streamExec('ROLLBACK').catch(()=>{});
|
|
2507
|
+
throw e;
|
|
2423
2508
|
}
|
|
2424
|
-
return out;
|
|
2425
2509
|
}
|
|
2426
2510
|
begin(fn) {
|
|
2427
2511
|
return this.transaction(fn);
|
|
2428
2512
|
}
|
|
2429
|
-
savepoint(fn) {
|
|
2430
|
-
return this.transaction(fn);
|
|
2513
|
+
async savepoint(fn) {
|
|
2514
|
+
if (null == this.streamId) return this.transaction(fn);
|
|
2515
|
+
const name = `sp_${this.client.allocStream()}`;
|
|
2516
|
+
await this.streamExec(`SAVEPOINT ${name}`);
|
|
2517
|
+
try {
|
|
2518
|
+
const out = await fn(this);
|
|
2519
|
+
await this.streamExec(`RELEASE SAVEPOINT ${name}`);
|
|
2520
|
+
return out;
|
|
2521
|
+
} catch (e) {
|
|
2522
|
+
await this.streamExec(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
|
|
2523
|
+
throw e;
|
|
2524
|
+
}
|
|
2431
2525
|
}
|
|
2432
2526
|
async ensureNamespaces(namespaces) {
|
|
2433
2527
|
return {
|
|
@@ -2465,20 +2559,26 @@ var __webpack_exports__ = {};
|
|
|
2465
2559
|
}
|
|
2466
2560
|
async isHealthy() {
|
|
2467
2561
|
try {
|
|
2468
|
-
await Promise.race([
|
|
2562
|
+
const res = await Promise.race([
|
|
2469
2563
|
this.client.pipeline('_health', [
|
|
2470
2564
|
{
|
|
2471
|
-
type: '
|
|
2565
|
+
type: 'get_version'
|
|
2472
2566
|
}
|
|
2473
2567
|
]),
|
|
2474
2568
|
new Promise((_, rej)=>setTimeout(()=>rej(new Error('probe timeout')), 1500))
|
|
2475
2569
|
]);
|
|
2570
|
+
const proto = res?.[0]?.response?.protocol;
|
|
2571
|
+
if (proto !== CLIENT_SOCKET_PROTOCOL) {
|
|
2572
|
+
console.warn(`[odb-client] socket protocol mismatch (server=${proto ?? 'none'}, client=${CLIENT_SOCKET_PROTOCOL}) → using HTTP`);
|
|
2573
|
+
return false;
|
|
2574
|
+
}
|
|
2476
2575
|
return true;
|
|
2477
2576
|
} catch {
|
|
2478
2577
|
return false;
|
|
2479
2578
|
}
|
|
2480
2579
|
}
|
|
2481
2580
|
}
|
|
2581
|
+
const CLIENT_SOCKET_PROTOCOL = 2;
|
|
2482
2582
|
const DEFAULT_UNIX_SOCKET = '/tmp/odblite.sock';
|
|
2483
2583
|
function parseSocketTarget(s) {
|
|
2484
2584
|
const v = (s ?? '').trim();
|
|
@@ -2499,13 +2599,26 @@ var __webpack_exports__ = {};
|
|
|
2499
2599
|
}
|
|
2500
2600
|
class OdbliteAutoAdapter {
|
|
2501
2601
|
type = 'odblite';
|
|
2602
|
+
opts;
|
|
2502
2603
|
chosen;
|
|
2604
|
+
onHttp = false;
|
|
2605
|
+
nextRetryAt = 0;
|
|
2503
2606
|
constructor(opts){
|
|
2504
|
-
this.
|
|
2607
|
+
this.opts = opts;
|
|
2608
|
+
this.chosen = this.pick();
|
|
2505
2609
|
}
|
|
2506
|
-
async pick(
|
|
2610
|
+
async pick() {
|
|
2507
2611
|
const hasBun = void 0 !== globalThis.Bun && !!globalThis.Bun.connect;
|
|
2508
|
-
if (hasBun)
|
|
2612
|
+
if (hasBun) {
|
|
2613
|
+
const socket = await this.trySocket();
|
|
2614
|
+
if (socket) return socket;
|
|
2615
|
+
}
|
|
2616
|
+
this.onHttp = true;
|
|
2617
|
+
console.log('[odb-client] transport → HTTP (no router_v2 socket reachable)');
|
|
2618
|
+
return new ODBLiteAdapter(this.opts.http);
|
|
2619
|
+
}
|
|
2620
|
+
async trySocket() {
|
|
2621
|
+
for (const target of this.candidates())try {
|
|
2509
2622
|
const socket = new OdbliteSocketAdapter(target);
|
|
2510
2623
|
if (await socket.isHealthy()) {
|
|
2511
2624
|
console.log(`[odb-client] transport → SOCKET (router_v2 @ ${target.unix ?? `${target.hostname}:${target.port}`})`);
|
|
@@ -2513,12 +2626,23 @@ var __webpack_exports__ = {};
|
|
|
2513
2626
|
}
|
|
2514
2627
|
await socket.disconnect();
|
|
2515
2628
|
} catch {}
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2629
|
+
return null;
|
|
2630
|
+
}
|
|
2631
|
+
async maybeUpgrade() {
|
|
2632
|
+
if (!this.onHttp) return;
|
|
2633
|
+
const now = Date.now();
|
|
2634
|
+
if (now < this.nextRetryAt) return;
|
|
2635
|
+
this.nextRetryAt = now + 15000;
|
|
2636
|
+
const socket = await this.trySocket();
|
|
2637
|
+
if (socket) {
|
|
2638
|
+
this.chosen = Promise.resolve(socket);
|
|
2639
|
+
this.onHttp = false;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
candidates() {
|
|
2643
|
+
const { socket, http } = this.opts;
|
|
2644
|
+
if (socket?.unix || socket?.port) return [
|
|
2645
|
+
socket
|
|
2522
2646
|
];
|
|
2523
2647
|
const env = globalThis.process?.env?.ODB_SOCKET;
|
|
2524
2648
|
if (env) return [
|
|
@@ -2530,7 +2654,7 @@ var __webpack_exports__ = {};
|
|
|
2530
2654
|
}
|
|
2531
2655
|
];
|
|
2532
2656
|
try {
|
|
2533
|
-
const host = new URL(
|
|
2657
|
+
const host = new URL(http?.serviceUrl).hostname;
|
|
2534
2658
|
const port = Number(globalThis.process?.env?.ODB_SOCKET_PORT ?? 8787);
|
|
2535
2659
|
if (host) out.push({
|
|
2536
2660
|
hostname: host,
|
|
@@ -2540,6 +2664,7 @@ var __webpack_exports__ = {};
|
|
|
2540
2664
|
return out;
|
|
2541
2665
|
}
|
|
2542
2666
|
async connect(config) {
|
|
2667
|
+
await this.maybeUpgrade();
|
|
2543
2668
|
return (await this.chosen).connect(config);
|
|
2544
2669
|
}
|
|
2545
2670
|
async disconnect(tenantId) {
|