@memberjunction/generic-database-provider 6.1.2 → 6.1.3

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.
@@ -0,0 +1,72 @@
1
+ import type { PostCommitToken } from '@memberjunction/core';
2
+ /** How a settled outermost transaction ended. */
3
+ export type TransactionEpochOutcome = 'committed' | 'rolledBack';
4
+ /**
5
+ * What to do with a post-commit task registered with a {@link PostCommitToken}.
6
+ * - `run` — the token's transaction committed; run the task now.
7
+ * - `queue` — the token's transaction is still open; queue at `Depth` (the deepest captured frame
8
+ * that is still open — a released savepoint's work belongs to its enclosing frame).
9
+ * - `drop` — the work was rolled back, or the token cannot be matched to known state.
10
+ */
11
+ export type PostCommitTokenResolution = {
12
+ Kind: 'run';
13
+ } | {
14
+ Kind: 'queue';
15
+ Depth: number;
16
+ }
17
+ /** `Unknown` marks a drop that is NOT a known rollback — the caller should log it louder. */
18
+ | {
19
+ Kind: 'drop';
20
+ Reason: string;
21
+ Unknown?: boolean;
22
+ };
23
+ /**
24
+ * Gives every transaction frame on one provider an identity, so work captured inside a frame can be
25
+ * matched to that frame's fate after the fact.
26
+ *
27
+ * - An **epoch** is one outermost transaction. Epoch and frame ids come from process-wide counters,
28
+ * so a token from a different provider instance never matches this one's state by accident.
29
+ * - The **frame stack** mirrors the provider's transaction depth: index 0 is the outermost
30
+ * transaction, each later entry a savepoint.
31
+ * - Savepoint frames rolled back in the open epoch are remembered until the epoch ends.
32
+ * - The outcomes of the last {@link SettledEpochLimit} epochs are remembered; older ones are
33
+ * forgotten, and a token naming one is treated as unknown (dropped).
34
+ *
35
+ * The owner must call the mutators in step with its own depth changes. Not thread-safe beyond what
36
+ * the owner's transaction lock provides; {@link Capture} and {@link Resolve} are synchronous.
37
+ */
38
+ export declare class TransactionFrameTracker {
39
+ /** How many settled epochs are remembered. */
40
+ static readonly SettledEpochLimit = 1000;
41
+ private static _lastEpoch;
42
+ private static _lastFrameId;
43
+ private _openEpoch;
44
+ private _frames;
45
+ private readonly _rolledBackFrames;
46
+ private readonly _settledEpochs;
47
+ /** Number of open frames. */
48
+ get Depth(): number;
49
+ /** A frame was begun. The first frame opens a new epoch. */
50
+ PushFrame(): void;
51
+ /** The innermost frame ended without undoing its work (savepoint released, or never created). */
52
+ ReleaseFrame(): void;
53
+ /** The innermost frame's work was undone (savepoint rolled back, or its frame dropped as doomed). */
54
+ RollBackFrame(): void;
55
+ /** The outermost transaction settled. No-op when no epoch is open. */
56
+ EndEpoch(outcome: TransactionEpochOutcome): void;
57
+ /**
58
+ * Snapshot of the open frames. Outside a transaction this returns a token with a `null` epoch
59
+ * rather than `undefined`: the work is already durable, and saying so is what stops a later
60
+ * registration from being attached to an unrelated transaction that opened in the meantime.
61
+ */
62
+ Capture(): PostCommitToken;
63
+ /**
64
+ * Decide what a task registered with `token` should do now.
65
+ * @param doomed Whether the owner's open transaction can no longer commit.
66
+ */
67
+ Resolve(token: PostCommitToken, doomed: boolean): PostCommitTokenResolution;
68
+ private rolledBackFrame;
69
+ private resolveOpenEpoch;
70
+ private evictOldEpochs;
71
+ }
72
+ //# sourceMappingURL=TransactionFrameTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TransactionFrameTracker.d.ts","sourceRoot":"","sources":["../src/TransactionFrameTracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,iDAAiD;AACjD,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG,YAAY,CAAC;AAYjE;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAClC,6FAA6F;GAC3F;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAuB;IAChC,8CAA8C;IAC9C,gBAAuB,iBAAiB,QAAQ;IAEhD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAK;IAC9B,OAAO,CAAC,MAAM,CAAC,YAAY,CAAK;IAEhC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmC;IAElE,6BAA6B;IAC7B,IAAW,KAAK,IAAI,MAAM,CAEzB;IAED,4DAA4D;IACrD,SAAS,IAAI,IAAI;IASxB,iGAAiG;IAC1F,YAAY,IAAI,IAAI;IAI3B,qGAAqG;IAC9F,aAAa,IAAI,IAAI;IAO5B,sEAAsE;IAC/D,QAAQ,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAWvD;;;;OAIG;IACI,OAAO,IAAI,eAAe;IAOjC;;;OAGG;IACI,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,GAAG,yBAAyB;IA0BlF,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,cAAc;CASzB"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Gives every transaction frame on one provider an identity, so work captured inside a frame can be
3
+ * matched to that frame's fate after the fact.
4
+ *
5
+ * - An **epoch** is one outermost transaction. Epoch and frame ids come from process-wide counters,
6
+ * so a token from a different provider instance never matches this one's state by accident.
7
+ * - The **frame stack** mirrors the provider's transaction depth: index 0 is the outermost
8
+ * transaction, each later entry a savepoint.
9
+ * - Savepoint frames rolled back in the open epoch are remembered until the epoch ends.
10
+ * - The outcomes of the last {@link SettledEpochLimit} epochs are remembered; older ones are
11
+ * forgotten, and a token naming one is treated as unknown (dropped).
12
+ *
13
+ * The owner must call the mutators in step with its own depth changes. Not thread-safe beyond what
14
+ * the owner's transaction lock provides; {@link Capture} and {@link Resolve} are synchronous.
15
+ */
16
+ export class TransactionFrameTracker {
17
+ constructor() {
18
+ this._openEpoch = null;
19
+ this._frames = [];
20
+ this._rolledBackFrames = new Set();
21
+ this._settledEpochs = new Map();
22
+ }
23
+ /** How many settled epochs are remembered. */
24
+ static { this.SettledEpochLimit = 1000; }
25
+ static { this._lastEpoch = 0; }
26
+ static { this._lastFrameId = 0; }
27
+ /** Number of open frames. */
28
+ get Depth() {
29
+ return this._frames.length;
30
+ }
31
+ /** A frame was begun. The first frame opens a new epoch. */
32
+ PushFrame() {
33
+ if (this._frames.length === 0) {
34
+ // An epoch with no frames left is one whose begin failed; it never committed.
35
+ this.EndEpoch('rolledBack');
36
+ this._openEpoch = ++TransactionFrameTracker._lastEpoch;
37
+ }
38
+ this._frames.push(++TransactionFrameTracker._lastFrameId);
39
+ }
40
+ /** The innermost frame ended without undoing its work (savepoint released, or never created). */
41
+ ReleaseFrame() {
42
+ this._frames.pop();
43
+ }
44
+ /** The innermost frame's work was undone (savepoint rolled back, or its frame dropped as doomed). */
45
+ RollBackFrame() {
46
+ const id = this._frames.pop();
47
+ if (id !== undefined) {
48
+ this._rolledBackFrames.add(id);
49
+ }
50
+ }
51
+ /** The outermost transaction settled. No-op when no epoch is open. */
52
+ EndEpoch(outcome) {
53
+ if (this._openEpoch === null) {
54
+ return;
55
+ }
56
+ this._settledEpochs.set(this._openEpoch, { Outcome: outcome, RolledBackFrames: new Set(this._rolledBackFrames) });
57
+ this.evictOldEpochs();
58
+ this._openEpoch = null;
59
+ this._frames = [];
60
+ this._rolledBackFrames.clear();
61
+ }
62
+ /**
63
+ * Snapshot of the open frames. Outside a transaction this returns a token with a `null` epoch
64
+ * rather than `undefined`: the work is already durable, and saying so is what stops a later
65
+ * registration from being attached to an unrelated transaction that opened in the meantime.
66
+ */
67
+ Capture() {
68
+ if (this._openEpoch === null || this._frames.length === 0) {
69
+ return { Epoch: null, FrameIds: [] };
70
+ }
71
+ return { Epoch: this._openEpoch, FrameIds: [...this._frames] };
72
+ }
73
+ /**
74
+ * Decide what a task registered with `token` should do now.
75
+ * @param doomed Whether the owner's open transaction can no longer commit.
76
+ */
77
+ Resolve(token, doomed) {
78
+ if (token.Epoch === null) {
79
+ // Caused outside any transaction: already durable, so nothing to wait for or be undone by.
80
+ return { Kind: 'run' };
81
+ }
82
+ if (token.Epoch === this._openEpoch) {
83
+ return this.resolveOpenEpoch(token, doomed);
84
+ }
85
+ const settled = this._settledEpochs.get(token.Epoch);
86
+ if (settled?.Outcome === 'committed') {
87
+ // The outer transaction committing does not resurrect a savepoint that rolled back
88
+ // inside it, so the captured frames still decide.
89
+ return this.rolledBackFrame(token, settled.RolledBackFrames)
90
+ ? { Kind: 'drop', Reason: 'the savepoint it was registered in rolled back' }
91
+ : { Kind: 'run' };
92
+ }
93
+ if (settled?.Outcome === 'rolledBack') {
94
+ return { Kind: 'drop', Reason: 'the transaction it was registered in rolled back' };
95
+ }
96
+ return {
97
+ Kind: 'drop',
98
+ Unknown: true,
99
+ Reason: `transaction ${token.Epoch} is unknown to this provider (another instance's, or older than the last ${TransactionFrameTracker.SettledEpochLimit} transactions)`,
100
+ };
101
+ }
102
+ rolledBackFrame(token, rolledBack) {
103
+ return token.FrameIds.some((id) => rolledBack.has(id));
104
+ }
105
+ resolveOpenEpoch(token, doomed) {
106
+ if (doomed) {
107
+ return { Kind: 'drop', Reason: 'the transaction it was registered in was abandoned' };
108
+ }
109
+ if (this.rolledBackFrame(token, this._rolledBackFrames)) {
110
+ return { Kind: 'drop', Reason: 'the savepoint it was registered in rolled back' };
111
+ }
112
+ for (let i = token.FrameIds.length - 1; i >= 0; i--) {
113
+ const index = this._frames.indexOf(token.FrameIds[i]);
114
+ if (index >= 0) {
115
+ return { Kind: 'queue', Depth: index + 1 };
116
+ }
117
+ }
118
+ return { Kind: 'drop', Reason: 'none of the frames it was registered in is still open' };
119
+ }
120
+ evictOldEpochs() {
121
+ while (this._settledEpochs.size > TransactionFrameTracker.SettledEpochLimit) {
122
+ const oldest = this._settledEpochs.keys().next();
123
+ if (oldest.done) {
124
+ return;
125
+ }
126
+ this._settledEpochs.delete(oldest.value);
127
+ }
128
+ }
129
+ }
130
+ //# sourceMappingURL=TransactionFrameTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TransactionFrameTracker.js","sourceRoot":"","sources":["../src/TransactionFrameTracker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/generic-database-provider",
3
3
  "type": "module",
4
- "version": "6.1.2",
4
+ "version": "6.1.3",
5
5
  "description": "Shared database provider logic for MemberJunction — intermediate base class between DatabaseProviderBase and platform-specific providers",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -16,19 +16,19 @@
16
16
  "vitest": "^3.1.1"
17
17
  },
18
18
  "dependencies": {
19
- "@memberjunction/actions": "6.1.2",
20
- "@memberjunction/actions-base": "6.1.2",
21
- "@memberjunction/ai-vectors-memory": "6.1.2",
22
- "@memberjunction/aiengine": "6.1.2",
23
- "@memberjunction/core": "6.1.2",
24
- "@memberjunction/core-entities": "6.1.2",
25
- "@memberjunction/encryption": "6.1.2",
26
- "@memberjunction/geo-core": "6.1.2",
27
- "@memberjunction/global": "6.1.2",
28
- "@memberjunction/query-processor": "6.1.2",
29
- "@memberjunction/sql-dialect": "6.1.2",
30
- "@memberjunction/sql-parser": "6.1.2",
31
- "@memberjunction/queue": "6.1.2",
19
+ "@memberjunction/actions": "6.1.3",
20
+ "@memberjunction/actions-base": "6.1.3",
21
+ "@memberjunction/ai-vectors-memory": "6.1.3",
22
+ "@memberjunction/aiengine": "6.1.3",
23
+ "@memberjunction/core": "6.1.3",
24
+ "@memberjunction/core-entities": "6.1.3",
25
+ "@memberjunction/encryption": "6.1.3",
26
+ "@memberjunction/geo-core": "6.1.3",
27
+ "@memberjunction/global": "6.1.3",
28
+ "@memberjunction/query-processor": "6.1.3",
29
+ "@memberjunction/sql-dialect": "6.1.3",
30
+ "@memberjunction/sql-parser": "6.1.3",
31
+ "@memberjunction/queue": "6.1.3",
32
32
  "sql-formatter": "^15.7.0",
33
33
  "uuid": "^13.0.0"
34
34
  },