@lossless.org/client 1.22.2 → 1.23.1
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 +41 -1
- 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.session.d.ts +7 -6
- package/dist_ts/nosqldb/classes.session.js +88 -36
- 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 +40 -4
- package/dist_ts/nosqldb/classes.writerevisions.js +174 -37
- 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 +64 -0
- package/ts/nosqldb/classes.doc.ts +4 -4
- package/ts/nosqldb/classes.exactpersistence.ts +8 -7
- package/ts/nosqldb/classes.session.ts +107 -38
- package/ts/nosqldb/classes.writelog.ts +701 -0
- package/ts/nosqldb/classes.writeobserver.ts +62 -28
- package/ts/nosqldb/classes.writerevisions.ts +225 -41
- package/ts/nosqldb/engineidentity.ts +16 -0
|
@@ -21,6 +21,7 @@ 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 {
|
|
@@ -36,13 +37,22 @@ import {
|
|
|
36
37
|
type ISmartdataReadSpec,
|
|
37
38
|
} from './classes.readtracker.js';
|
|
38
39
|
import {
|
|
39
|
-
|
|
40
|
+
beginSmartdataWriteRevisionAttempt,
|
|
40
41
|
flushSmartdataWriteRevisions,
|
|
41
42
|
isSmartdataWriteRevisionsActive,
|
|
43
|
+
type ISmartdataWriteRevisionAttempt,
|
|
42
44
|
readPreviousRevisionIds,
|
|
43
45
|
sharedRevisionId,
|
|
44
46
|
writeRevisionsPartitionField,
|
|
45
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';
|
|
46
56
|
|
|
47
57
|
interface ISmartdataSessionBinding {
|
|
48
58
|
session: SmartdataSession;
|
|
@@ -72,6 +82,12 @@ interface ISmartdataSessionBinding {
|
|
|
72
82
|
transactionWrites: ISmartdataTransactionWrite[];
|
|
73
83
|
/** The write-revision counters the current attempt's writes bump right before its commit. */
|
|
74
84
|
revisionBumps: Set<string>;
|
|
85
|
+
/** The slots of those counters the current attempt holds. */
|
|
86
|
+
revisionAttempt?: ISmartdataWriteRevisionAttempt;
|
|
87
|
+
/** The reports of the current attempt's writes, which its write-log entry carries. */
|
|
88
|
+
logReports: ISmartdataWriteReport[];
|
|
89
|
+
/** Collections the current attempt changed in ways no report describes. */
|
|
90
|
+
logUnplaced: Set<string>;
|
|
75
91
|
operationSettled?: Promise<void>;
|
|
76
92
|
resolveOperationSettled?: () => void;
|
|
77
93
|
invalidated: boolean;
|
|
@@ -363,10 +379,17 @@ export class SmartdataSession {
|
|
|
363
379
|
let attemptInterruption: { error: unknown } | undefined;
|
|
364
380
|
/** The session-ended rejection the last attempt failed with, and how far that transaction had got. */
|
|
365
381
|
let sessionEnded: { error: SmartdataPersistenceError; end: TSmartdataTransactionEnd } | undefined;
|
|
366
|
-
let
|
|
382
|
+
let revisionAttempt: ISmartdataWriteRevisionAttempt | undefined;
|
|
383
|
+
/** The last attempt's write-log entry until the transaction's outcome is known. */
|
|
384
|
+
let logCommit: ISmartdataWriteLogCommit | undefined;
|
|
367
385
|
const runAttempt = async () => {
|
|
368
|
-
|
|
369
|
-
|
|
386
|
+
// The last attempt's slots end after this one starts, so it bumps others.
|
|
387
|
+
const attemptStartedAt = performance.now();
|
|
388
|
+
revisionAttempt?.finish(false);
|
|
389
|
+
revisionAttempt = beginSmartdataWriteRevisionAttempt(binding.owner, attemptStartedAt);
|
|
390
|
+
// The driver starts another attempt only once the last one was aborted.
|
|
391
|
+
logCommit?.settle();
|
|
392
|
+
logCommit = undefined;
|
|
370
393
|
const attempt = Symbol('smartdataTransactionAttempt');
|
|
371
394
|
binding.transactionAttempt = attempt;
|
|
372
395
|
binding.transactionCallbackOpen = true;
|
|
@@ -375,6 +398,9 @@ export class SmartdataSession {
|
|
|
375
398
|
binding.transactionCommitReached = false;
|
|
376
399
|
binding.transactionWrites = [];
|
|
377
400
|
binding.revisionBumps = new Set();
|
|
401
|
+
binding.revisionAttempt = revisionAttempt;
|
|
402
|
+
binding.logReports = [];
|
|
403
|
+
binding.logUnplaced = new Set();
|
|
378
404
|
callbackError = undefined;
|
|
379
405
|
attemptInterruption = undefined;
|
|
380
406
|
try {
|
|
@@ -426,17 +452,22 @@ export class SmartdataSession {
|
|
|
426
452
|
}
|
|
427
453
|
// An abort that arrived while the callback ran rolls the attempt back.
|
|
428
454
|
signal?.throwIfAborted();
|
|
429
|
-
// The writes' revisions commit with them. A
|
|
430
|
-
// before its commit, and a transient one is
|
|
455
|
+
// The writes' revisions and write-log entry commit with them. A
|
|
456
|
+
// failure fails the attempt before its commit, and a transient one is
|
|
457
|
+
// retried with it.
|
|
458
|
+
const mongoDb = binding.client.db(binding.owner.smartdataOptions.mongoDbName);
|
|
431
459
|
if (binding.revisionBumps.size > 0) {
|
|
432
|
-
|
|
433
|
-
await flushSmartdataWriteRevisions(
|
|
434
|
-
binding.client.db(binding.owner.smartdataOptions.mongoDbName),
|
|
435
|
-
binding.rawSession,
|
|
436
|
-
binding.revisionBumps,
|
|
437
|
-
signal,
|
|
438
|
-
);
|
|
460
|
+
revisionAttempt.commit();
|
|
461
|
+
await flushSmartdataWriteRevisions(mongoDb, binding.rawSession, revisionAttempt.slots, signal);
|
|
439
462
|
}
|
|
463
|
+
logCommit = await flushSmartdataWriteLog(
|
|
464
|
+
binding.owner,
|
|
465
|
+
mongoDb,
|
|
466
|
+
binding.rawSession,
|
|
467
|
+
binding.logReports,
|
|
468
|
+
binding.logUnplaced,
|
|
469
|
+
signal,
|
|
470
|
+
);
|
|
440
471
|
// The driver commits next; a deadline that expires or a session that
|
|
441
472
|
// ends from here on leaves the commit outcome unknown.
|
|
442
473
|
binding.transactionCommitReached = true;
|
|
@@ -530,7 +561,10 @@ export class SmartdataSession {
|
|
|
530
561
|
if (outcome === 'unknown') changeReadsOfTransaction(binding.transactionWrites);
|
|
531
562
|
throw rejectTransaction(error, outcome);
|
|
532
563
|
} finally {
|
|
533
|
-
if (!returnedFromCommit)
|
|
564
|
+
if (!returnedFromCommit) {
|
|
565
|
+
revisionAttempt?.finish(false);
|
|
566
|
+
logCommit?.settle();
|
|
567
|
+
}
|
|
534
568
|
binding.transactionActive = false;
|
|
535
569
|
binding.transactionTimeoutMS = undefined;
|
|
536
570
|
binding.transactionSignal = undefined;
|
|
@@ -540,24 +574,33 @@ export class SmartdataSession {
|
|
|
540
574
|
binding.transactionEnded = undefined;
|
|
541
575
|
binding.transactionWrites = [];
|
|
542
576
|
binding.revisionBumps = new Set();
|
|
577
|
+
binding.revisionAttempt = undefined;
|
|
578
|
+
binding.logReports = [];
|
|
579
|
+
binding.logUnplaced = new Set();
|
|
543
580
|
}
|
|
544
|
-
|
|
545
|
-
|
|
581
|
+
try {
|
|
582
|
+
if (transactionEnded) {
|
|
583
|
+
const outcome = commitOutcomeOfTransactionEnd(transactionEnded);
|
|
584
|
+
try {
|
|
585
|
+
if (outcome === 'unknown') changeReadsOfTransaction(committedWrites);
|
|
586
|
+
} finally {
|
|
587
|
+
revisionAttempt?.finish(false);
|
|
588
|
+
}
|
|
589
|
+
throw rejectTransaction(createTransactionSessionEndedError(transactionEnded), outcome);
|
|
590
|
+
}
|
|
546
591
|
try {
|
|
547
|
-
|
|
592
|
+
changeReadsOfTransaction(committedWrites);
|
|
593
|
+
accountSmartdataOwnWriteRevisions(binding.owner, committedRevisionBumps);
|
|
548
594
|
} finally {
|
|
549
|
-
|
|
595
|
+
revisionAttempt?.finish(true);
|
|
550
596
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
597
|
+
for (const write of committedWrites) {
|
|
598
|
+
write.deliver?.();
|
|
599
|
+
}
|
|
600
|
+
// Followers hear of the commit before they may read it from the journal.
|
|
601
|
+
if (logCommit) deliverSmartdataWriteLogCommit(binding.owner, logCommit);
|
|
556
602
|
} finally {
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
for (const write of committedWrites) {
|
|
560
|
-
write.deliver?.();
|
|
603
|
+
logCommit?.settle();
|
|
561
604
|
}
|
|
562
605
|
return result;
|
|
563
606
|
}
|
|
@@ -598,6 +641,8 @@ export const createSmartdataSession = (ownerArg: SmartdataDb): SmartdataSession
|
|
|
598
641
|
transactionCommitReached: false,
|
|
599
642
|
transactionWrites: [],
|
|
600
643
|
revisionBumps: new Set(),
|
|
644
|
+
logReports: [],
|
|
645
|
+
logUnplaced: new Set(),
|
|
601
646
|
invalidated: false,
|
|
602
647
|
};
|
|
603
648
|
bindings.set(session, binding);
|
|
@@ -854,18 +899,19 @@ const revisionTransactionSessionOf = (ownerArg: SmartdataDb): SmartdataSession |
|
|
|
854
899
|
|
|
855
900
|
/**
|
|
856
901
|
* Whether a write must start a transaction of its own to carry its write
|
|
857
|
-
* revisions: the database keeps
|
|
858
|
-
* transaction. A raw driver session cannot carry them and is
|
|
902
|
+
* revisions and write-log entry: the database keeps either and the write runs
|
|
903
|
+
* outside an owned transaction. A raw driver session cannot carry them and is
|
|
904
|
+
* refused.
|
|
859
905
|
*/
|
|
860
|
-
export const
|
|
906
|
+
export const needsSmartdataWriteTransaction = (
|
|
861
907
|
sessionArg: TSmartdataOrdinarySession | undefined,
|
|
862
908
|
ownerArg: SmartdataDb,
|
|
863
909
|
): boolean => {
|
|
864
|
-
if (!
|
|
910
|
+
if (!requiresSmartdataWriteTransactions(ownerArg)) return false;
|
|
865
911
|
if (sessionArg !== undefined && !isSmartdataSession(sessionArg)) {
|
|
866
912
|
throw new SmartdataPersistenceError(
|
|
867
913
|
'unsupported_operation',
|
|
868
|
-
'Writes of a database with write revisions run on sessions of db.createSession(), not raw driver sessions.',
|
|
914
|
+
'Writes of a database with write revisions or a write log run on sessions of db.createSession(), not raw driver sessions.',
|
|
869
915
|
);
|
|
870
916
|
}
|
|
871
917
|
return sessionArg === undefined || !resolveBinding(sessionArg, ownerArg).transactionActive;
|
|
@@ -880,13 +926,13 @@ const transactionOptionsOf = (
|
|
|
880
926
|
});
|
|
881
927
|
|
|
882
928
|
/**
|
|
883
|
-
* Runs one write of a database with write revisions
|
|
884
|
-
* revisions commit with it: on the caller's owned session, or on a session of
|
|
929
|
+
* Runs one write of a database with write revisions or a write log in a
|
|
930
|
+
* transaction, so its revisions and entry commit with it: on the caller's owned session, or on a session of
|
|
885
931
|
* its own that ends with the call. The call's deadline and signal bound the
|
|
886
932
|
* whole transaction; the collection is prepared before it starts, since its
|
|
887
933
|
* DDL cannot run inside.
|
|
888
934
|
*/
|
|
889
|
-
export const
|
|
935
|
+
export const runSmartdataWriteTransaction = async <TResult>(
|
|
890
936
|
sessionArg: SmartdataSession | undefined,
|
|
891
937
|
ownerArg: SmartdataDb,
|
|
892
938
|
optionsArg: Pick<
|
|
@@ -936,8 +982,8 @@ export const runWithOrdinarySmartdataSession = async <TResult>(
|
|
|
936
982
|
) => Promise<TResult>,
|
|
937
983
|
): Promise<TResult> => {
|
|
938
984
|
optionsArg.signal?.throwIfAborted();
|
|
939
|
-
if (optionsArg.ordinaryWrite &&
|
|
940
|
-
return
|
|
985
|
+
if (optionsArg.ordinaryWrite && needsSmartdataWriteTransaction(sessionArg, ownerArg)) {
|
|
986
|
+
return runSmartdataWriteTransaction(
|
|
941
987
|
sessionArg as SmartdataSession | undefined,
|
|
942
988
|
ownerArg,
|
|
943
989
|
optionsArg,
|
|
@@ -985,6 +1031,21 @@ const bufferTransactionWrite = (
|
|
|
985
1031
|
}
|
|
986
1032
|
};
|
|
987
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Adds a write's report, or a collection no report describes, to the
|
|
1036
|
+
* write-log entry of an owned transaction's current attempt. A write that
|
|
1037
|
+
* settles after its attempt ended rolls back with that attempt.
|
|
1038
|
+
*/
|
|
1039
|
+
const logTransactionWrite = (
|
|
1040
|
+
bindingArg: ISmartdataSessionBinding,
|
|
1041
|
+
writeArg: { report: ISmartdataWriteReport } | { unplaced: string },
|
|
1042
|
+
): void => {
|
|
1043
|
+
const attempt = transactionContext.getStore()?.attempt;
|
|
1044
|
+
if (attempt === undefined || attempt !== bindingArg.transactionAttempt) return;
|
|
1045
|
+
if ('report' in writeArg) bindingArg.logReports.push(writeArg.report);
|
|
1046
|
+
else bindingArg.logUnplaced.add(writeArg.unplaced);
|
|
1047
|
+
};
|
|
1048
|
+
|
|
988
1049
|
/**
|
|
989
1050
|
* Adds write-revision counters to the current attempt of an owned transaction.
|
|
990
1051
|
* A write that settles after its attempt ended rolls back with that attempt.
|
|
@@ -995,7 +1056,10 @@ const bumpTransactionRevisions = (
|
|
|
995
1056
|
): void => {
|
|
996
1057
|
const attempt = transactionContext.getStore()?.attempt;
|
|
997
1058
|
if (attempt === undefined || attempt !== bindingArg.transactionAttempt) return;
|
|
998
|
-
for (const id of idsArg)
|
|
1059
|
+
for (const id of idsArg) {
|
|
1060
|
+
bindingArg.revisionBumps.add(id);
|
|
1061
|
+
bindingArg.revisionAttempt?.bump(id);
|
|
1062
|
+
}
|
|
999
1063
|
};
|
|
1000
1064
|
|
|
1001
1065
|
/**
|
|
@@ -1046,6 +1110,8 @@ export const reportSmartdataWrite = (
|
|
|
1046
1110
|
if (field !== undefined && isSmartdataWriteRevisionsActive(ownerArg)) {
|
|
1047
1111
|
bumpTransactionRevisions(binding, writeRevisionIdsOfWrite(writeArg, field));
|
|
1048
1112
|
}
|
|
1113
|
+
const report = writeLogReportOf(ownerArg, writeArg);
|
|
1114
|
+
if (report) logTransactionWrite(binding, { report });
|
|
1049
1115
|
}
|
|
1050
1116
|
const changeReads = captureSmartdataReadChange(ownerArg, writeArg, buffered);
|
|
1051
1117
|
const deliver = captureSmartdataWrite(ownerArg, writeArg);
|
|
@@ -1074,6 +1140,9 @@ export const reportSmartdataWriteFailure = (
|
|
|
1074
1140
|
if (binding && buffered && isSmartdataWriteRevisionsActive(ownerArg)) {
|
|
1075
1141
|
bumpTransactionRevisions(binding, [sharedRevisionId(collectionNameArg)]);
|
|
1076
1142
|
}
|
|
1143
|
+
if (binding && buffered && isSmartdataWriteLogActive(ownerArg)) {
|
|
1144
|
+
logTransactionWrite(binding, { unplaced: collectionNameArg });
|
|
1145
|
+
}
|
|
1077
1146
|
const changeReads = captureSmartdataCollectionChange(ownerArg, collectionNameArg, buffered);
|
|
1078
1147
|
if (!changeReads) return;
|
|
1079
1148
|
if (binding && buffered) {
|