@lossless.org/client 1.22.1 → 1.23.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/nosqldb/classes.bootstraprecord.js +4 -4
- package/dist_ts/nosqldb/classes.connection.d.ts +3 -0
- package/dist_ts/nosqldb/classes.connection.js +3 -2
- package/dist_ts/nosqldb/classes.db.d.ts +31 -0
- package/dist_ts/nosqldb/classes.db.js +45 -2
- package/dist_ts/nosqldb/classes.doc.js +5 -5
- package/dist_ts/nosqldb/classes.exactpersistence.d.ts +3 -2
- package/dist_ts/nosqldb/classes.exactpersistence.js +8 -7
- package/dist_ts/nosqldb/classes.readtracker.d.ts +6 -4
- package/dist_ts/nosqldb/classes.readtracker.js +42 -10
- package/dist_ts/nosqldb/classes.session.d.ts +7 -6
- package/dist_ts/nosqldb/classes.session.js +91 -23
- package/dist_ts/nosqldb/classes.writelog.d.ts +129 -0
- package/dist_ts/nosqldb/classes.writelog.js +497 -0
- package/dist_ts/nosqldb/classes.writeobserver.d.ts +13 -0
- package/dist_ts/nosqldb/classes.writeobserver.js +47 -25
- package/dist_ts/nosqldb/classes.writerevisions.d.ts +18 -7
- package/dist_ts/nosqldb/classes.writerevisions.js +179 -30
- package/dist_ts/nosqldb/engineidentity.d.ts +6 -0
- package/dist_ts/nosqldb/engineidentity.js +14 -1
- package/package.json +2 -2
- package/readme.md +11 -10
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/nosqldb/classes.bootstraprecord.ts +3 -3
- package/ts/nosqldb/classes.connection.ts +5 -1
- package/ts/nosqldb/classes.db.ts +68 -0
- package/ts/nosqldb/classes.doc.ts +4 -4
- package/ts/nosqldb/classes.exactpersistence.ts +8 -7
- package/ts/nosqldb/classes.readtracker.ts +41 -7
- package/ts/nosqldb/classes.session.ts +107 -26
- package/ts/nosqldb/classes.writelog.ts +701 -0
- package/ts/nosqldb/classes.writeobserver.ts +62 -28
- package/ts/nosqldb/classes.writerevisions.ts +224 -32
- package/ts/nosqldb/engineidentity.ts +16 -0
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
captureSmartdataWrite,
|
|
22
22
|
hasSmartdataWriteObservers,
|
|
23
23
|
type ISmartdataWrite,
|
|
24
|
+
type ISmartdataWriteReport,
|
|
24
25
|
type TSmartdataWriteKind,
|
|
25
26
|
} from './classes.writeobserver.js';
|
|
26
27
|
import {
|
|
28
|
+
accountSmartdataOwnWriteRevisions,
|
|
27
29
|
captureSmartdataCollectionChange,
|
|
28
30
|
captureSmartdataReadChange,
|
|
29
31
|
hasSmartdataReadScope,
|
|
@@ -35,12 +37,22 @@ import {
|
|
|
35
37
|
type ISmartdataReadSpec,
|
|
36
38
|
} from './classes.readtracker.js';
|
|
37
39
|
import {
|
|
40
|
+
beginSmartdataWriteRevisionCommit,
|
|
38
41
|
flushSmartdataWriteRevisions,
|
|
39
42
|
isSmartdataWriteRevisionsActive,
|
|
43
|
+
noteSmartdataWriteRevisionEntries,
|
|
40
44
|
readPreviousRevisionIds,
|
|
41
45
|
sharedRevisionId,
|
|
42
46
|
writeRevisionsPartitionField,
|
|
43
47
|
} from './classes.writerevisions.js';
|
|
48
|
+
import {
|
|
49
|
+
deliverSmartdataWriteLogCommit,
|
|
50
|
+
flushSmartdataWriteLog,
|
|
51
|
+
isSmartdataWriteLogActive,
|
|
52
|
+
requiresSmartdataWriteTransactions,
|
|
53
|
+
writeLogReportOf,
|
|
54
|
+
type ISmartdataWriteLogCommit,
|
|
55
|
+
} from './classes.writelog.js';
|
|
44
56
|
|
|
45
57
|
interface ISmartdataSessionBinding {
|
|
46
58
|
session: SmartdataSession;
|
|
@@ -70,6 +82,10 @@ interface ISmartdataSessionBinding {
|
|
|
70
82
|
transactionWrites: ISmartdataTransactionWrite[];
|
|
71
83
|
/** The write-revision counters the current attempt's writes bump right before its commit. */
|
|
72
84
|
revisionBumps: Set<string>;
|
|
85
|
+
/** The reports of the current attempt's writes, which its write-log entry carries. */
|
|
86
|
+
logReports: ISmartdataWriteReport[];
|
|
87
|
+
/** Collections the current attempt changed in ways no report describes. */
|
|
88
|
+
logUnplaced: Set<string>;
|
|
73
89
|
operationSettled?: Promise<void>;
|
|
74
90
|
resolveOperationSettled?: () => void;
|
|
75
91
|
invalidated: boolean;
|
|
@@ -361,7 +377,15 @@ export class SmartdataSession {
|
|
|
361
377
|
let attemptInterruption: { error: unknown } | undefined;
|
|
362
378
|
/** The session-ended rejection the last attempt failed with, and how far that transaction had got. */
|
|
363
379
|
let sessionEnded: { error: SmartdataPersistenceError; end: TSmartdataTransactionEnd } | undefined;
|
|
380
|
+
let finishRevisionCommit: (() => void) | undefined;
|
|
381
|
+
/** The last attempt's write-log entry until the transaction's outcome is known. */
|
|
382
|
+
let logCommit: ISmartdataWriteLogCommit | undefined;
|
|
364
383
|
const runAttempt = async () => {
|
|
384
|
+
finishRevisionCommit?.();
|
|
385
|
+
finishRevisionCommit = undefined;
|
|
386
|
+
// The driver starts another attempt only once the last one was aborted.
|
|
387
|
+
logCommit?.settle();
|
|
388
|
+
logCommit = undefined;
|
|
365
389
|
const attempt = Symbol('smartdataTransactionAttempt');
|
|
366
390
|
binding.transactionAttempt = attempt;
|
|
367
391
|
binding.transactionCallbackOpen = true;
|
|
@@ -370,6 +394,8 @@ export class SmartdataSession {
|
|
|
370
394
|
binding.transactionCommitReached = false;
|
|
371
395
|
binding.transactionWrites = [];
|
|
372
396
|
binding.revisionBumps = new Set();
|
|
397
|
+
binding.logReports = [];
|
|
398
|
+
binding.logUnplaced = new Set();
|
|
373
399
|
callbackError = undefined;
|
|
374
400
|
attemptInterruption = undefined;
|
|
375
401
|
try {
|
|
@@ -421,16 +447,22 @@ export class SmartdataSession {
|
|
|
421
447
|
}
|
|
422
448
|
// An abort that arrived while the callback ran rolls the attempt back.
|
|
423
449
|
signal?.throwIfAborted();
|
|
424
|
-
// The writes' revisions commit with them. A
|
|
425
|
-
// before its commit, and a transient one is
|
|
450
|
+
// The writes' revisions and write-log entry commit with them. A
|
|
451
|
+
// failure fails the attempt before its commit, and a transient one is
|
|
452
|
+
// retried with it.
|
|
453
|
+
const mongoDb = binding.client.db(binding.owner.smartdataOptions.mongoDbName);
|
|
426
454
|
if (binding.revisionBumps.size > 0) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
binding.rawSession,
|
|
430
|
-
binding.revisionBumps,
|
|
431
|
-
signal,
|
|
432
|
-
);
|
|
455
|
+
finishRevisionCommit = beginSmartdataWriteRevisionCommit(binding.owner, binding.revisionBumps);
|
|
456
|
+
await flushSmartdataWriteRevisions(mongoDb, binding.rawSession, binding.revisionBumps, signal);
|
|
433
457
|
}
|
|
458
|
+
logCommit = await flushSmartdataWriteLog(
|
|
459
|
+
binding.owner,
|
|
460
|
+
mongoDb,
|
|
461
|
+
binding.rawSession,
|
|
462
|
+
binding.logReports,
|
|
463
|
+
binding.logUnplaced,
|
|
464
|
+
signal,
|
|
465
|
+
);
|
|
434
466
|
// The driver commits next; a deadline that expires or a session that
|
|
435
467
|
// ends from here on leaves the commit outcome unknown.
|
|
436
468
|
binding.transactionCommitReached = true;
|
|
@@ -509,17 +541,25 @@ export class SmartdataSession {
|
|
|
509
541
|
let result: TResult | undefined;
|
|
510
542
|
let transactionEnded: TSmartdataTransactionEnd | undefined;
|
|
511
543
|
let committedWrites: ISmartdataTransactionWrite[] = [];
|
|
544
|
+
let committedRevisionBumps: ReadonlySet<string> = new Set();
|
|
545
|
+
let returnedFromCommit = false;
|
|
512
546
|
try {
|
|
513
547
|
result = timeoutMS === undefined
|
|
514
548
|
? await binding.rawSession.withTransaction(runAttempt)
|
|
515
549
|
: await binding.rawSession.withTransaction(runAttempt, { timeoutMS });
|
|
516
550
|
transactionEnded = binding.transactionEnded;
|
|
517
551
|
committedWrites = binding.transactionWrites;
|
|
552
|
+
committedRevisionBumps = binding.revisionBumps;
|
|
553
|
+
returnedFromCommit = true;
|
|
518
554
|
} catch (errorArg) {
|
|
519
555
|
const { error, outcome } = settleTransactionRejection(errorArg);
|
|
520
556
|
if (outcome === 'unknown') changeReadsOfTransaction(binding.transactionWrites);
|
|
521
557
|
throw rejectTransaction(error, outcome);
|
|
522
558
|
} finally {
|
|
559
|
+
if (!returnedFromCommit) {
|
|
560
|
+
finishRevisionCommit?.();
|
|
561
|
+
logCommit?.settle();
|
|
562
|
+
}
|
|
523
563
|
binding.transactionActive = false;
|
|
524
564
|
binding.transactionTimeoutMS = undefined;
|
|
525
565
|
binding.transactionSignal = undefined;
|
|
@@ -529,15 +569,33 @@ export class SmartdataSession {
|
|
|
529
569
|
binding.transactionEnded = undefined;
|
|
530
570
|
binding.transactionWrites = [];
|
|
531
571
|
binding.revisionBumps = new Set();
|
|
572
|
+
binding.logReports = [];
|
|
573
|
+
binding.logUnplaced = new Set();
|
|
532
574
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
575
|
+
try {
|
|
576
|
+
if (transactionEnded) {
|
|
577
|
+
const outcome = commitOutcomeOfTransactionEnd(transactionEnded);
|
|
578
|
+
try {
|
|
579
|
+
if (outcome === 'unknown') changeReadsOfTransaction(committedWrites);
|
|
580
|
+
} finally {
|
|
581
|
+
finishRevisionCommit?.();
|
|
582
|
+
}
|
|
583
|
+
throw rejectTransaction(createTransactionSessionEndedError(transactionEnded), outcome);
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
changeReadsOfTransaction(committedWrites);
|
|
587
|
+
accountSmartdataOwnWriteRevisions(binding.owner, committedRevisionBumps);
|
|
588
|
+
} finally {
|
|
589
|
+
finishRevisionCommit?.();
|
|
590
|
+
}
|
|
591
|
+
noteSmartdataWriteRevisionEntries(binding.owner, committedRevisionBumps);
|
|
592
|
+
for (const write of committedWrites) {
|
|
593
|
+
write.deliver?.();
|
|
594
|
+
}
|
|
595
|
+
// Followers hear of the commit before they may read it from the journal.
|
|
596
|
+
if (logCommit) deliverSmartdataWriteLogCommit(binding.owner, logCommit);
|
|
597
|
+
} finally {
|
|
598
|
+
logCommit?.settle();
|
|
541
599
|
}
|
|
542
600
|
return result;
|
|
543
601
|
}
|
|
@@ -578,6 +636,8 @@ export const createSmartdataSession = (ownerArg: SmartdataDb): SmartdataSession
|
|
|
578
636
|
transactionCommitReached: false,
|
|
579
637
|
transactionWrites: [],
|
|
580
638
|
revisionBumps: new Set(),
|
|
639
|
+
logReports: [],
|
|
640
|
+
logUnplaced: new Set(),
|
|
581
641
|
invalidated: false,
|
|
582
642
|
};
|
|
583
643
|
bindings.set(session, binding);
|
|
@@ -834,18 +894,19 @@ const revisionTransactionSessionOf = (ownerArg: SmartdataDb): SmartdataSession |
|
|
|
834
894
|
|
|
835
895
|
/**
|
|
836
896
|
* Whether a write must start a transaction of its own to carry its write
|
|
837
|
-
* revisions: the database keeps
|
|
838
|
-
* transaction. A raw driver session cannot carry them and is
|
|
897
|
+
* revisions and write-log entry: the database keeps either and the write runs
|
|
898
|
+
* outside an owned transaction. A raw driver session cannot carry them and is
|
|
899
|
+
* refused.
|
|
839
900
|
*/
|
|
840
|
-
export const
|
|
901
|
+
export const needsSmartdataWriteTransaction = (
|
|
841
902
|
sessionArg: TSmartdataOrdinarySession | undefined,
|
|
842
903
|
ownerArg: SmartdataDb,
|
|
843
904
|
): boolean => {
|
|
844
|
-
if (!
|
|
905
|
+
if (!requiresSmartdataWriteTransactions(ownerArg)) return false;
|
|
845
906
|
if (sessionArg !== undefined && !isSmartdataSession(sessionArg)) {
|
|
846
907
|
throw new SmartdataPersistenceError(
|
|
847
908
|
'unsupported_operation',
|
|
848
|
-
'Writes of a database with write revisions run on sessions of db.createSession(), not raw driver sessions.',
|
|
909
|
+
'Writes of a database with write revisions or a write log run on sessions of db.createSession(), not raw driver sessions.',
|
|
849
910
|
);
|
|
850
911
|
}
|
|
851
912
|
return sessionArg === undefined || !resolveBinding(sessionArg, ownerArg).transactionActive;
|
|
@@ -860,13 +921,13 @@ const transactionOptionsOf = (
|
|
|
860
921
|
});
|
|
861
922
|
|
|
862
923
|
/**
|
|
863
|
-
* Runs one write of a database with write revisions
|
|
864
|
-
* revisions commit with it: on the caller's owned session, or on a session of
|
|
924
|
+
* Runs one write of a database with write revisions or a write log in a
|
|
925
|
+
* transaction, so its revisions and entry commit with it: on the caller's owned session, or on a session of
|
|
865
926
|
* its own that ends with the call. The call's deadline and signal bound the
|
|
866
927
|
* whole transaction; the collection is prepared before it starts, since its
|
|
867
928
|
* DDL cannot run inside.
|
|
868
929
|
*/
|
|
869
|
-
export const
|
|
930
|
+
export const runSmartdataWriteTransaction = async <TResult>(
|
|
870
931
|
sessionArg: SmartdataSession | undefined,
|
|
871
932
|
ownerArg: SmartdataDb,
|
|
872
933
|
optionsArg: Pick<
|
|
@@ -916,8 +977,8 @@ export const runWithOrdinarySmartdataSession = async <TResult>(
|
|
|
916
977
|
) => Promise<TResult>,
|
|
917
978
|
): Promise<TResult> => {
|
|
918
979
|
optionsArg.signal?.throwIfAborted();
|
|
919
|
-
if (optionsArg.ordinaryWrite &&
|
|
920
|
-
return
|
|
980
|
+
if (optionsArg.ordinaryWrite && needsSmartdataWriteTransaction(sessionArg, ownerArg)) {
|
|
981
|
+
return runSmartdataWriteTransaction(
|
|
921
982
|
sessionArg as SmartdataSession | undefined,
|
|
922
983
|
ownerArg,
|
|
923
984
|
optionsArg,
|
|
@@ -965,6 +1026,21 @@ const bufferTransactionWrite = (
|
|
|
965
1026
|
}
|
|
966
1027
|
};
|
|
967
1028
|
|
|
1029
|
+
/**
|
|
1030
|
+
* Adds a write's report, or a collection no report describes, to the
|
|
1031
|
+
* write-log entry of an owned transaction's current attempt. A write that
|
|
1032
|
+
* settles after its attempt ended rolls back with that attempt.
|
|
1033
|
+
*/
|
|
1034
|
+
const logTransactionWrite = (
|
|
1035
|
+
bindingArg: ISmartdataSessionBinding,
|
|
1036
|
+
writeArg: { report: ISmartdataWriteReport } | { unplaced: string },
|
|
1037
|
+
): void => {
|
|
1038
|
+
const attempt = transactionContext.getStore()?.attempt;
|
|
1039
|
+
if (attempt === undefined || attempt !== bindingArg.transactionAttempt) return;
|
|
1040
|
+
if ('report' in writeArg) bindingArg.logReports.push(writeArg.report);
|
|
1041
|
+
else bindingArg.logUnplaced.add(writeArg.unplaced);
|
|
1042
|
+
};
|
|
1043
|
+
|
|
968
1044
|
/**
|
|
969
1045
|
* Adds write-revision counters to the current attempt of an owned transaction.
|
|
970
1046
|
* A write that settles after its attempt ended rolls back with that attempt.
|
|
@@ -1026,6 +1102,8 @@ export const reportSmartdataWrite = (
|
|
|
1026
1102
|
if (field !== undefined && isSmartdataWriteRevisionsActive(ownerArg)) {
|
|
1027
1103
|
bumpTransactionRevisions(binding, writeRevisionIdsOfWrite(writeArg, field));
|
|
1028
1104
|
}
|
|
1105
|
+
const report = writeLogReportOf(ownerArg, writeArg);
|
|
1106
|
+
if (report) logTransactionWrite(binding, { report });
|
|
1029
1107
|
}
|
|
1030
1108
|
const changeReads = captureSmartdataReadChange(ownerArg, writeArg, buffered);
|
|
1031
1109
|
const deliver = captureSmartdataWrite(ownerArg, writeArg);
|
|
@@ -1054,6 +1132,9 @@ export const reportSmartdataWriteFailure = (
|
|
|
1054
1132
|
if (binding && buffered && isSmartdataWriteRevisionsActive(ownerArg)) {
|
|
1055
1133
|
bumpTransactionRevisions(binding, [sharedRevisionId(collectionNameArg)]);
|
|
1056
1134
|
}
|
|
1135
|
+
if (binding && buffered && isSmartdataWriteLogActive(ownerArg)) {
|
|
1136
|
+
logTransactionWrite(binding, { unplaced: collectionNameArg });
|
|
1137
|
+
}
|
|
1057
1138
|
const changeReads = captureSmartdataCollectionChange(ownerArg, collectionNameArg, buffered);
|
|
1058
1139
|
if (!changeReads) return;
|
|
1059
1140
|
if (binding && buffered) {
|