@rotorsoft/act-sqlite 1.22.7 → 2.0.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/README.md +4 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +2 -2
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/index.cjs +62 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -49
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
} from "@libsql/client";
|
|
5
5
|
import {
|
|
6
6
|
ConcurrencyError,
|
|
7
|
+
check_appends,
|
|
8
|
+
DEFAULT_QUERY_STREAMS_LIMIT,
|
|
9
|
+
fair_slots,
|
|
7
10
|
is_literal_source,
|
|
8
11
|
StoreError,
|
|
9
12
|
ValidationError
|
|
@@ -241,8 +244,9 @@ var SqliteStore = class {
|
|
|
241
244
|
// while a write transaction is open on it: a second one fails at once
|
|
242
245
|
// (`TRANSACTION_ACTIVE` in memory, `SQLITE_BUSY` on a file) instead of
|
|
243
246
|
// waiting. So every statement and every transaction takes its turn here.
|
|
244
|
-
// A turn covers only the database work,
|
|
245
|
-
//
|
|
247
|
+
// A turn covers only the database work, so a `query` callback that writes to
|
|
248
|
+
// the store can't wait on itself. `restore` is the exception: its driver runs
|
|
249
|
+
// inside the turn, because the whole restore is one transaction.
|
|
246
250
|
_turn = Promise.resolve();
|
|
247
251
|
_serial(work) {
|
|
248
252
|
const run = this._turn.then(work);
|
|
@@ -276,53 +280,63 @@ var SqliteStore = class {
|
|
|
276
280
|
async dispose() {
|
|
277
281
|
await this._serial(async () => this.client.close());
|
|
278
282
|
}
|
|
279
|
-
// --- commit: transaction
|
|
280
|
-
async commit(
|
|
283
|
+
// --- commit: one transaction across every append ---
|
|
284
|
+
async commit(appends, meta) {
|
|
285
|
+
check_appends(appends);
|
|
286
|
+
if (!appends.length) return [];
|
|
281
287
|
return this._serial(async () => {
|
|
282
288
|
const tx = await this._begin("commit");
|
|
283
|
-
let
|
|
289
|
+
let appending = appends[0];
|
|
290
|
+
let head = -1;
|
|
284
291
|
try {
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
292
|
+
const heads = /* @__PURE__ */ new Map();
|
|
293
|
+
for (const { stream, msgs, expectedVersion } of appends) {
|
|
294
|
+
const row = await tx.execute({
|
|
295
|
+
sql: "SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?",
|
|
296
|
+
args: [stream]
|
|
297
|
+
});
|
|
298
|
+
const current = Number(row.rows[0].v);
|
|
299
|
+
if (typeof expectedVersion === "number" && current !== expectedVersion)
|
|
300
|
+
throw new ConcurrencyError(
|
|
301
|
+
stream,
|
|
302
|
+
current,
|
|
303
|
+
msgs,
|
|
304
|
+
expectedVersion
|
|
305
|
+
);
|
|
306
|
+
heads.set(stream, current);
|
|
297
307
|
}
|
|
298
308
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
309
|
const committed = [];
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
310
|
+
for (const append of appends) {
|
|
311
|
+
appending = append;
|
|
312
|
+
head = heads.get(append.stream);
|
|
313
|
+
let version = head + 1;
|
|
314
|
+
for (const { name, data, pii } of append.msgs) {
|
|
315
|
+
const pii_for_write = await this._stringify_pii_for_write(pii);
|
|
316
|
+
const result = await tx.execute({
|
|
317
|
+
sql: "INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
318
|
+
args: [
|
|
319
|
+
append.stream,
|
|
320
|
+
version,
|
|
321
|
+
name,
|
|
322
|
+
JSON.stringify(data),
|
|
323
|
+
JSON.stringify(meta),
|
|
324
|
+
now,
|
|
325
|
+
pii_for_write
|
|
326
|
+
]
|
|
327
|
+
});
|
|
328
|
+
committed.push({
|
|
329
|
+
id: Number(result.lastInsertRowid),
|
|
330
|
+
stream: append.stream,
|
|
307
331
|
version,
|
|
332
|
+
created: new Date(now),
|
|
308
333
|
name,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
committed.push({
|
|
316
|
-
id: Number(result.lastInsertRowid),
|
|
317
|
-
stream,
|
|
318
|
-
version,
|
|
319
|
-
created: new Date(now),
|
|
320
|
-
name,
|
|
321
|
-
data,
|
|
322
|
-
meta,
|
|
323
|
-
...pii == null ? {} : { pii }
|
|
324
|
-
});
|
|
325
|
-
version++;
|
|
334
|
+
data,
|
|
335
|
+
meta,
|
|
336
|
+
...pii == null ? {} : { pii }
|
|
337
|
+
});
|
|
338
|
+
version++;
|
|
339
|
+
}
|
|
326
340
|
}
|
|
327
341
|
await tx.commit();
|
|
328
342
|
return committed;
|
|
@@ -331,10 +345,10 @@ var SqliteStore = class {
|
|
|
331
345
|
if (e instanceof ConcurrencyError) throw e;
|
|
332
346
|
if (is_unique_violation(e))
|
|
333
347
|
throw new ConcurrencyError(
|
|
334
|
-
stream,
|
|
335
|
-
|
|
336
|
-
msgs,
|
|
337
|
-
expectedVersion ?? -1
|
|
348
|
+
appending.stream,
|
|
349
|
+
head,
|
|
350
|
+
appending.msgs,
|
|
351
|
+
appending.expectedVersion ?? -1
|
|
338
352
|
);
|
|
339
353
|
throw new StoreError("commit", { cause: e });
|
|
340
354
|
}
|
|
@@ -407,8 +421,8 @@ var SqliteStore = class {
|
|
|
407
421
|
// targeting the same stream. Operator overrides go
|
|
408
422
|
// through `prioritize()` instead.
|
|
409
423
|
async subscribe(streams, correlated_at, correlator) {
|
|
410
|
-
for (const { source } of streams) {
|
|
411
|
-
if (source !== void 0 && !is_literal_source(source))
|
|
424
|
+
for (const { stream, source } of streams) {
|
|
425
|
+
if (source !== void 0 && !is_literal_source(source, stream))
|
|
412
426
|
streamPatternToGlob(source);
|
|
413
427
|
}
|
|
414
428
|
return this._tx("subscribe", async (tx) => {
|
|
@@ -542,7 +556,7 @@ var SqliteStore = class {
|
|
|
542
556
|
priority: Number(row.priority),
|
|
543
557
|
lane: row.lane
|
|
544
558
|
})).sort((a, b) => b.priority - a.priority || a.at - b.at);
|
|
545
|
-
const fair =
|
|
559
|
+
const fair = fair_slots(lagging);
|
|
546
560
|
const priority_slice = candidates.slice(0, lagging - fair);
|
|
547
561
|
const picked = new Set(priority_slice.map((c) => c.stream));
|
|
548
562
|
const fair_slice = [...candidates].sort((a, b) => a.at - b.at).filter((c) => !picked.has(c.stream)).slice(0, fair);
|
|
@@ -749,7 +763,7 @@ var SqliteStore = class {
|
|
|
749
763
|
}
|
|
750
764
|
// --- query_streams: read-only introspection with filters ---
|
|
751
765
|
async query_streams(callback, query) {
|
|
752
|
-
const limit = query?.limit ??
|
|
766
|
+
const limit = query?.limit ?? DEFAULT_QUERY_STREAMS_LIMIT;
|
|
753
767
|
const { clause, args } = this._filter_clause(query ?? {});
|
|
754
768
|
let sql = `SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at, correlated_at FROM streams WHERE ${clause}`;
|
|
755
769
|
if (query?.after !== void 0) {
|
|
@@ -1064,6 +1078,8 @@ var SqliteStore = class {
|
|
|
1064
1078
|
* new sequence is dense from 1. `created` is preserved verbatim
|
|
1065
1079
|
* from the source.
|
|
1066
1080
|
*/
|
|
1081
|
+
// The driver runs inside this store's turn, so it must read from another
|
|
1082
|
+
// store: a call on this one would wait for the restore to end, forever.
|
|
1067
1083
|
async restore(driver) {
|
|
1068
1084
|
return this._serial(async () => {
|
|
1069
1085
|
const tx = await this._begin("restore");
|