nesoi 3.3.30 → 3.4.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/lib/elements/edge/controller/adapters/controller_adapter.js +1 -3
- package/lib/elements/entities/message/template/message_template_parser.d.ts +5 -1
- package/lib/elements/entities/message/template/message_template_parser.js +13 -7
- package/lib/engine/daemon.d.ts +1 -11
- package/lib/engine/daemon.js +3 -26
- package/lib/engine/data/datetime.d.ts +35 -1
- package/lib/engine/data/datetime.js +103 -16
- package/lib/engine/data/error.d.ts +5 -0
- package/lib/engine/data/error.js +4 -0
- package/lib/engine/transaction/nodes/bucket.trx_node.d.ts +2 -0
- package/lib/engine/transaction/nodes/bucket.trx_node.js +12 -0
- package/lib/engine/transaction/nodes/bucket_query.trx_node.js +1 -1
- package/lib/engine/transaction/nodes/external.trx_node.js +15 -18
- package/lib/engine/transaction/nodes/job.trx_node.d.ts +4 -3
- package/lib/engine/transaction/nodes/job.trx_node.js +6 -1
- package/lib/engine/transaction/nodes/resource.trx_node.js +2 -1
- package/lib/engine/transaction/trx.d.ts +4 -3
- package/lib/engine/transaction/trx.js +15 -11
- package/lib/engine/transaction/trx_engine.config.d.ts +7 -3
- package/lib/engine/transaction/trx_engine.d.ts +7 -3
- package/lib/engine/transaction/trx_engine.js +101 -45
- package/lib/engine/transaction/trx_node.d.ts +4 -1
- package/lib/engine/transaction/trx_node.js +12 -9
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -22,8 +22,9 @@ class TrxStatus {
|
|
|
22
22
|
state;
|
|
23
23
|
output;
|
|
24
24
|
error;
|
|
25
|
+
idempotent;
|
|
25
26
|
nodes;
|
|
26
|
-
constructor(id, origin, start, end, state, output, error, nodes = []) {
|
|
27
|
+
constructor(id, origin, start, end, state, output, error, idempotent = false, nodes = []) {
|
|
27
28
|
this.id = id;
|
|
28
29
|
this.origin = origin;
|
|
29
30
|
this.start = start;
|
|
@@ -31,10 +32,11 @@ class TrxStatus {
|
|
|
31
32
|
this.state = state;
|
|
32
33
|
this.output = output;
|
|
33
34
|
this.error = error;
|
|
35
|
+
this.idempotent = idempotent;
|
|
34
36
|
this.nodes = nodes;
|
|
35
37
|
}
|
|
36
38
|
summary() {
|
|
37
|
-
const state = this.state ? (0, string_1.colored)(`[${this.state}]`, {
|
|
39
|
+
const state = this.state ? (0, string_1.colored)(`[${this.state}]${this.idempotent ? '*' : ''}`, {
|
|
38
40
|
'open': 'lightblue',
|
|
39
41
|
'hold': 'yellow',
|
|
40
42
|
'ok': 'lightgreen',
|
|
@@ -42,10 +44,13 @@ class TrxStatus {
|
|
|
42
44
|
}[this.state]) : 'unknown';
|
|
43
45
|
let str = `${state} ${this.id} ${(0, log_1.anyScopeTag)(this.origin)} `;
|
|
44
46
|
str += (0, string_1.colored)(`[${this.end ? (this.end.epoch - this.start.epoch) : -1}ms]\n`, 'brown');
|
|
45
|
-
function print(nodes, l = 1) {
|
|
47
|
+
function print(nodes, idempotent, l = 1) {
|
|
46
48
|
let str = '';
|
|
47
49
|
nodes.forEach(node => {
|
|
48
|
-
|
|
50
|
+
if (node.ext?.idempotent !== undefined) {
|
|
51
|
+
idempotent = node.ext.idempotent;
|
|
52
|
+
}
|
|
53
|
+
const state = node.state ? (0, string_1.colored)(`[${node.state}]${idempotent ? '*' : ''}`, {
|
|
49
54
|
'open': 'lightblue',
|
|
50
55
|
'hold': 'yellow',
|
|
51
56
|
'ok': 'lightgreen',
|
|
@@ -53,11 +58,11 @@ class TrxStatus {
|
|
|
53
58
|
}[node.state] || 'lightred') : 'unknown';
|
|
54
59
|
str += `${'-'.repeat(l)}${state} ${node.id} ${(0, log_1.anyScopeTag)(node.scope)} ${node.action} ${node.cached_buckets > 0 ? `[cached: ${node.cached_buckets}]` : ''}`;
|
|
55
60
|
str += (0, string_1.colored)(` [${node.app}ms]\n`, 'brown');
|
|
56
|
-
str += print(node.nodes, l + 1);
|
|
61
|
+
str += print(node.nodes, idempotent, l + 1);
|
|
57
62
|
});
|
|
58
63
|
return str;
|
|
59
64
|
}
|
|
60
|
-
return str + print(this.nodes);
|
|
65
|
+
return str + print(this.nodes, this.idempotent);
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
exports.TrxStatus = TrxStatus;
|
|
@@ -83,7 +88,7 @@ class Trx {
|
|
|
83
88
|
this.id = id || (Math.random() + 1).toString(36).substring(7);
|
|
84
89
|
this.origin = origin;
|
|
85
90
|
this.idempotent = idempotent ?? false;
|
|
86
|
-
this.root = root || new trx_node_1.TrxNode('root', this, undefined, module, auth, false
|
|
91
|
+
this.root = root || new trx_node_1.TrxNode('root', this, undefined, module, auth, false);
|
|
87
92
|
this.nodes = nodes || {};
|
|
88
93
|
}
|
|
89
94
|
addNode(node) {
|
|
@@ -94,7 +99,7 @@ class Trx {
|
|
|
94
99
|
const state = this.root.state;
|
|
95
100
|
const output = this.root.output;
|
|
96
101
|
const error = this.root.error;
|
|
97
|
-
return new TrxStatus(this.id, this.origin, this.start, this.end, state, output, error, this.root.status().nodes);
|
|
102
|
+
return new TrxStatus(this.id, this.origin, this.start, this.end, state, output, error, this.idempotent, this.root.status().nodes);
|
|
98
103
|
}
|
|
99
104
|
/**
|
|
100
105
|
* Cache
|
|
@@ -146,13 +151,12 @@ class Trx {
|
|
|
146
151
|
return trx;
|
|
147
152
|
}
|
|
148
153
|
//
|
|
149
|
-
static async
|
|
154
|
+
static async commitHolds(trx) {
|
|
150
155
|
for (const h in trx.holds) {
|
|
151
156
|
await trx.holds[h].commit();
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
|
-
static async
|
|
155
|
-
trx.end = datetime_1.NesoiDatetime.now();
|
|
159
|
+
static async rollbackHolds(trx) {
|
|
156
160
|
for (const h in trx.holds) {
|
|
157
161
|
const error = `rollback ${trx.id}`;
|
|
158
162
|
await trx.holds[h].rollback(error);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $Module, $Space } from "../../elements";
|
|
1
|
+
import { $Bucket, $Module, $Space } from "../../elements";
|
|
2
2
|
import { BucketAdapter } from "../../elements/entities/bucket/adapters/bucket_adapter";
|
|
3
3
|
import { Trx } from './trx';
|
|
4
4
|
import { AnyUsers } from '../auth/authn';
|
|
@@ -7,9 +7,13 @@ import { TrxData } from './trx_engine';
|
|
|
7
7
|
export type TrxEngineWrapFn<S extends $Space, M extends $Module> = (trx: TrxNode<S, M, any>) => Promise<any>;
|
|
8
8
|
export type TrxEngineConfig<S extends $Space, M extends $Module, AuthUsers extends AnyUsers, Services extends Record<string, any>> = {
|
|
9
9
|
/**
|
|
10
|
-
* Adapter used to store transactions of this module.
|
|
10
|
+
* Adapter used to temporarily store transactions of this module, while they happen.
|
|
11
11
|
*/
|
|
12
|
-
adapter?: (schema:
|
|
12
|
+
adapter?: (schema: $Bucket) => BucketAdapter<TrxData>;
|
|
13
|
+
/**
|
|
14
|
+
* Adapter used to log transactions of this module once they're finished.
|
|
15
|
+
*/
|
|
16
|
+
log_adapter?: (schema: $Bucket) => BucketAdapter<TrxData>;
|
|
13
17
|
wrap?: {
|
|
14
18
|
begin: <T extends Trx<S, M, AuthUsers>>(trx: T, services: Services) => Promise<void>;
|
|
15
19
|
continue: <T extends Trx<S, M, AuthUsers>>(trx: T, services: Services) => Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $Module, $Space } from "../../schema";
|
|
2
2
|
import { Module } from '../module';
|
|
3
3
|
import { AnyTrx, Trx, TrxStatus } from './trx';
|
|
4
|
-
import { TrxNode, TrxNodeStatus } from './trx_node';
|
|
4
|
+
import { TrxNode, TrxNodeState, TrxNodeStatus } from './trx_node';
|
|
5
5
|
import { AnyAuthnProviders, AnyUsers, AuthRequest } from '../auth/authn';
|
|
6
6
|
import { BucketAdapterConfig } from "../../elements/entities/bucket/adapters/bucket_adapter";
|
|
7
7
|
import { TrxEngineConfig } from './trx_engine.config';
|
|
@@ -12,6 +12,7 @@ import { DriveAdapter } from "../../elements/entities/drive/drive_adapter";
|
|
|
12
12
|
export type TrxEngineOrigin = `app:${string}` | `plugin:${string}`;
|
|
13
13
|
export type TrxData = {
|
|
14
14
|
id: AnyTrx['id'];
|
|
15
|
+
state: TrxNodeState;
|
|
15
16
|
origin: AnyTrx['origin'];
|
|
16
17
|
module: string;
|
|
17
18
|
start: AnyTrx['start'];
|
|
@@ -43,16 +44,19 @@ export declare class TrxEngine<S extends $Space, M extends $Module, AuthUsers ex
|
|
|
43
44
|
*/
|
|
44
45
|
private innerTrx;
|
|
45
46
|
private adapter;
|
|
47
|
+
private log_adapter?;
|
|
46
48
|
constructor(origin: TrxEngineOrigin, module: Module<S, M>, authnProviders?: AuthUsers | undefined, config?: TrxEngineConfig<S, M, any, any> | undefined, services?: Record<string, IService>);
|
|
47
49
|
getModule(): Module<S, M>;
|
|
48
|
-
get(id?: string, origin?: string,
|
|
50
|
+
get(id?: string, origin?: string, req_idempotent?: boolean): Promise<Trx<S, M, any>>;
|
|
49
51
|
trx(fn: (trx: TrxNode<S, M, any>) => Promise<TrxNodeStatus>, id?: string, tokens?: AuthRequest<keyof AuthUsers>, users?: Partial<AuthUsers>, origin?: string, idempotent?: boolean): Promise<TrxStatus<any>>;
|
|
50
|
-
trx_hold(fn: (trx: TrxNode<S, M, any>) => Promise<any>, id?: string, authn?: AuthRequest<keyof AuthUsers>, users?: Partial<AuthUsers>, origin?: string
|
|
52
|
+
trx_hold(fn: (trx: TrxNode<S, M, any>) => Promise<any>, id?: string, authn?: AuthRequest<keyof AuthUsers>, users?: Partial<AuthUsers>, origin?: string): Promise<HeldTrxNode<any>>;
|
|
51
53
|
getBucketMetadata(tag: Tag): BucketMetadata;
|
|
52
54
|
getBucketDrive(tag: Tag): DriveAdapter | undefined;
|
|
53
55
|
authenticate(node: TrxNode<S, M, any>, tokens?: AuthRequest<keyof AuthUsers>, users?: AnyUsers, force?: boolean): Promise<void>;
|
|
54
56
|
private hold;
|
|
57
|
+
private ok;
|
|
55
58
|
private commit;
|
|
59
|
+
private error;
|
|
56
60
|
private rollback;
|
|
57
61
|
}
|
|
58
62
|
export type AnyTrxEngine = TrxEngine<any, any, any>;
|
|
@@ -26,6 +26,7 @@ class TrxEngine {
|
|
|
26
26
|
*/
|
|
27
27
|
innerTrx;
|
|
28
28
|
adapter;
|
|
29
|
+
log_adapter;
|
|
29
30
|
constructor(origin, module, authnProviders, config, services = {}) {
|
|
30
31
|
this.origin = origin;
|
|
31
32
|
this.module = module;
|
|
@@ -35,29 +36,35 @@ class TrxEngine {
|
|
|
35
36
|
this.innerTrx = new trx_1.Trx(this, this.module, `trx:${origin}`, true);
|
|
36
37
|
this.$TrxBucket = new elements_1.$Bucket(this.module.name, '__trx__', `Transaction of Module '${this.module.name}'`, new bucket_model_schema_1.$BucketModel({
|
|
37
38
|
id: new bucket_model_schema_1.$BucketModelField('id', 'id', 'string', 'ID', true),
|
|
39
|
+
state: new bucket_model_schema_1.$BucketModelField('state', 'state', 'string', 'State', true), // TrxNodeState
|
|
38
40
|
origin: new bucket_model_schema_1.$BucketModelField('origin', 'origin', 'string', 'Origin', true),
|
|
39
41
|
module: new bucket_model_schema_1.$BucketModelField('module', 'module', 'string', 'Module', true),
|
|
40
42
|
start: new bucket_model_schema_1.$BucketModelField('start', 'start', 'datetime', 'Start', true),
|
|
41
|
-
end: new bucket_model_schema_1.$BucketModelField('end', 'end', 'datetime', 'End', false)
|
|
43
|
+
end: new bucket_model_schema_1.$BucketModelField('end', 'end', 'datetime', 'End', false)
|
|
42
44
|
}), new bucket_graph_schema_1.$BucketGraph(), {});
|
|
43
|
-
this.adapter = config?.adapter?.(
|
|
45
|
+
this.adapter = config?.adapter?.(this.$TrxBucket) || new memory_bucket_adapter_1.MemoryBucketAdapter(this.$TrxBucket, {});
|
|
46
|
+
if (config?.log_adapter) {
|
|
47
|
+
this.log_adapter = config?.log_adapter?.(this.$TrxBucket);
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
50
|
getModule() {
|
|
46
51
|
return this.module;
|
|
47
52
|
}
|
|
48
|
-
async get(id, origin,
|
|
53
|
+
async get(id, origin, req_idempotent = false) {
|
|
49
54
|
const _origin = origin ?? this.origin;
|
|
50
55
|
let trx;
|
|
51
56
|
// New transaction
|
|
52
57
|
if (!id) {
|
|
53
|
-
trx = new trx_1.Trx(this, this.module, _origin,
|
|
54
|
-
log_1.Log.info('module', this.module.name, `Begin${
|
|
58
|
+
trx = new trx_1.Trx(this, this.module, _origin, req_idempotent);
|
|
59
|
+
log_1.Log.info('module', this.module.name, `Begin${req_idempotent ? '*' : ''} ${(0, log_1.scopeTag)('trx', trx.root.globalId)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
55
60
|
for (const wrap of this.config?.wrap || []) {
|
|
61
|
+
// The wrappers decide how to begin a db transaction, based on the trx idempotent flag.
|
|
56
62
|
await wrap.begin(trx, this.services);
|
|
57
63
|
}
|
|
58
|
-
if (!
|
|
64
|
+
if (!req_idempotent) {
|
|
59
65
|
await this.adapter.create(this.innerTrx.root, {
|
|
60
66
|
id: trx.id,
|
|
67
|
+
state: 'open',
|
|
61
68
|
origin: trx.origin,
|
|
62
69
|
start: trx.start,
|
|
63
70
|
end: trx.end,
|
|
@@ -69,49 +76,78 @@ class TrxEngine {
|
|
|
69
76
|
// Chain/Continue transaction
|
|
70
77
|
else {
|
|
71
78
|
const trxData = await this.adapter.get(this.innerTrx.root, id);
|
|
79
|
+
// If trxData exists, the transaction to which it refers is non-idempotent,
|
|
80
|
+
// since idempotent transactions are not stored.
|
|
81
|
+
//
|
|
72
82
|
// If a transaction is being continued it cannot become idempotent,
|
|
73
83
|
// since the data it needs is inside the transaction.
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
if (trxData)
|
|
81
|
-
idempotent = false;
|
|
84
|
+
//
|
|
85
|
+
// If the transaction started as non-idempotent and is continued as idempotent,
|
|
86
|
+
// the code below will transform it into non-idempotent, however
|
|
87
|
+
// the engine.ok and engine.error still treat it as idempotent, to avoid commit/rollback
|
|
88
|
+
// of the original transaction.
|
|
89
|
+
//
|
|
82
90
|
// Differently, a transaction with specific id which doesn't exist can
|
|
83
91
|
// be either an ongoing idempotent transaction from this module or an
|
|
84
92
|
// external transaction starting at a different module.
|
|
85
|
-
// On either case, it's allowed to
|
|
86
|
-
|
|
87
|
-
// if (!trxData) idempotent = idempotent;
|
|
93
|
+
// On either case, it's allowed to be idempotent or not.
|
|
94
|
+
const idempotent = trxData ? false : req_idempotent;
|
|
88
95
|
trx = new trx_1.Trx(this, this.module, _origin, idempotent, undefined, id);
|
|
89
|
-
// (Continue*)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
// (Begin/Continue*)
|
|
97
|
+
// The request is for an idempotent transaction.
|
|
98
|
+
if (req_idempotent) {
|
|
99
|
+
// A non-idempotent transaction with the same id exists on this module.
|
|
100
|
+
// so it's being continued as an idempotent transaction.
|
|
101
|
+
if (trxData) {
|
|
102
|
+
log_1.Log.info('module', this.module.name, `Continue* ${(0, log_1.scopeTag)('trx', trx.root.globalId)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
103
|
+
if (trxData.state !== 'hold') {
|
|
104
|
+
throw new Error(`Attempt to continue transaction ${trxData.id}, currently at '${trxData.state}', failed. Should be at 'hold'. This might mean there are parallel attempts to continue a transaction, which must be handled with a queue.`);
|
|
105
|
+
}
|
|
106
|
+
for (const wrap of this.config?.wrap || []) {
|
|
107
|
+
// The wrappers decide how to continue a db transaction, based on the trx idempotent flag.
|
|
108
|
+
await wrap.continue(trx, this.services);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// No transaction with the same id exists on this module.
|
|
112
|
+
// so it's starting as an idempotent transaction.
|
|
113
|
+
else {
|
|
114
|
+
log_1.Log.info('module', this.module.name, `Begin* ${(0, log_1.scopeTag)('trx', trx.root.globalId)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
115
|
+
for (const wrap of this.config?.wrap || []) {
|
|
116
|
+
// The wrappers decide how to begin a db transaction, based on the trx idempotent flag.
|
|
117
|
+
await wrap.begin(trx, this.services);
|
|
118
|
+
}
|
|
94
119
|
}
|
|
95
120
|
return trx;
|
|
96
121
|
}
|
|
97
122
|
// (Continue)
|
|
123
|
+
// A non-idempotent transaction with the same id exists on this module,
|
|
124
|
+
// so it's being continued.
|
|
98
125
|
if (trxData) {
|
|
126
|
+
log_1.Log.info('module', this.module.name, `Continue ${(0, log_1.scopeTag)('trx', trx.root.globalId)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
127
|
+
if (trxData.state !== 'hold') {
|
|
128
|
+
throw new Error(`Attempt to continue transaction ${trxData.id}, currently at '${trxData.state}', failed. Should be at 'hold'. This might mean there are parallel attempts to continue a transaction, which must be handled with a queue.`);
|
|
129
|
+
}
|
|
99
130
|
// Update transaction with data read from adapter
|
|
100
131
|
trx.start = trxData.start;
|
|
101
132
|
trx.end = trxData.end;
|
|
102
|
-
log_1.Log.info('module', this.module.name, `Continue ${(0, log_1.scopeTag)('trx', trx.id + trx.root.id)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
103
133
|
for (const wrap of this.config?.wrap || []) {
|
|
134
|
+
// The wrappers decide how to continue a db transaction, based on the trx idempotent flag.
|
|
104
135
|
await wrap.continue(trx, this.services);
|
|
105
136
|
}
|
|
106
137
|
}
|
|
107
138
|
// (Chain)
|
|
139
|
+
// No transaction with the same id exists on this module,
|
|
140
|
+
// so it's starting as a "chained" transaction - a new one with
|
|
141
|
+
// the same ID as the one from some module.
|
|
108
142
|
else {
|
|
109
|
-
log_1.Log.info('module', this.module.name, `Chain ${(0, log_1.scopeTag)('trx',
|
|
143
|
+
log_1.Log.info('module', this.module.name, `Chain${trx.idempotent ? '*' : ''} ${(0, log_1.scopeTag)('trx', trx.root.globalId)} @ ${(0, log_1.anyScopeTag)(_origin)}`);
|
|
110
144
|
for (const wrap of this.config?.wrap || []) {
|
|
145
|
+
// The wrappers decide how to begin a db transaction, based on the trx idempotent flag.
|
|
111
146
|
await wrap.begin(trx, this.services);
|
|
112
147
|
}
|
|
113
148
|
await this.adapter.create(this.innerTrx.root, {
|
|
114
149
|
id: trx.id,
|
|
150
|
+
state: 'open',
|
|
115
151
|
origin: _origin,
|
|
116
152
|
start: trx.start,
|
|
117
153
|
end: trx.end,
|
|
@@ -126,29 +162,29 @@ class TrxEngine {
|
|
|
126
162
|
try {
|
|
127
163
|
await this.authenticate(trx.root, tokens, users);
|
|
128
164
|
const output = await fn(trx.root);
|
|
129
|
-
await this.
|
|
165
|
+
await this.ok(trx, output, idempotent);
|
|
130
166
|
}
|
|
131
167
|
catch (e) {
|
|
132
|
-
await this.
|
|
168
|
+
await this.error(trx, e, idempotent);
|
|
133
169
|
}
|
|
134
170
|
return trx.status();
|
|
135
171
|
}
|
|
136
|
-
async trx_hold(fn, id, authn, users, origin
|
|
137
|
-
const trx = await this.get(id, origin,
|
|
172
|
+
async trx_hold(fn, id, authn, users, origin) {
|
|
173
|
+
const trx = await this.get(id, origin, false);
|
|
138
174
|
let output = {};
|
|
139
175
|
try {
|
|
140
176
|
await this.authenticate(trx.root, authn, users);
|
|
141
177
|
output = await fn(trx.root);
|
|
142
|
-
await this.hold(trx, output
|
|
178
|
+
await this.hold(trx, output);
|
|
143
179
|
}
|
|
144
180
|
catch (e) {
|
|
145
|
-
await this.
|
|
181
|
+
await this.error(trx, e, false);
|
|
146
182
|
}
|
|
147
183
|
return {
|
|
148
184
|
id: trx.id,
|
|
149
185
|
status: trx.status(),
|
|
150
|
-
commit: () => this.
|
|
151
|
-
rollback: (error) => this.
|
|
186
|
+
commit: () => this.ok(trx, output, false),
|
|
187
|
+
rollback: (error) => this.error(trx, error, false)
|
|
152
188
|
};
|
|
153
189
|
}
|
|
154
190
|
/* Metadata sharing between modules */
|
|
@@ -188,13 +224,12 @@ class TrxEngine {
|
|
|
188
224
|
trx_node_1.TrxNode.addAuthn(node, _tokens, _users);
|
|
189
225
|
}
|
|
190
226
|
//
|
|
191
|
-
async hold(trx, output
|
|
192
|
-
log_1.Log.
|
|
227
|
+
async hold(trx, output) {
|
|
228
|
+
log_1.Log.info('module', this.module.name, `Hold ${(0, log_1.scopeTag)('trx', trx.root.globalId)}`);
|
|
193
229
|
trx_node_1.TrxNode.hold(trx.root, output);
|
|
194
|
-
if (idempotent)
|
|
195
|
-
return trx;
|
|
196
230
|
await this.adapter.put(this.innerTrx.root, {
|
|
197
231
|
id: trx.id,
|
|
232
|
+
state: 'hold',
|
|
198
233
|
origin: this.origin,
|
|
199
234
|
start: trx.start,
|
|
200
235
|
end: trx.end,
|
|
@@ -202,40 +237,61 @@ class TrxEngine {
|
|
|
202
237
|
});
|
|
203
238
|
return trx;
|
|
204
239
|
}
|
|
205
|
-
async
|
|
240
|
+
async ok(trx, output, idempotent) {
|
|
241
|
+
if (idempotent) {
|
|
242
|
+
log_1.Log.info('module', this.module.name, `Ok* ${(0, log_1.scopeTag)('trx', trx.root.globalId)}`);
|
|
243
|
+
}
|
|
206
244
|
trx_node_1.TrxNode.ok(trx.root, output);
|
|
207
245
|
trx.end = datetime_1.NesoiDatetime.now();
|
|
246
|
+
const held_children = Object.keys(trx.holds).length;
|
|
247
|
+
if (held_children) {
|
|
248
|
+
log_1.Log.debug('module', this.module.name, `Commit Holds ${(0, log_1.scopeTag)('trx', trx.root.globalId)} (${held_children})`);
|
|
249
|
+
await trx_1.Trx.commitHolds(trx);
|
|
250
|
+
}
|
|
208
251
|
if (idempotent)
|
|
209
252
|
return trx;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
253
|
+
return this.commit(trx);
|
|
254
|
+
}
|
|
255
|
+
async commit(trx) {
|
|
256
|
+
log_1.Log.info('module', this.module.name, `Commit ${(0, log_1.scopeTag)('trx', trx.root.globalId)}`);
|
|
257
|
+
await this.log_adapter?.put(this.innerTrx.root, {
|
|
213
258
|
id: trx.id,
|
|
259
|
+
state: 'ok',
|
|
214
260
|
origin: this.origin,
|
|
215
261
|
start: trx.start,
|
|
216
262
|
end: trx.end,
|
|
217
263
|
module: this.module.name,
|
|
218
264
|
});
|
|
265
|
+
await this.adapter.delete(this.innerTrx.root, trx.id);
|
|
219
266
|
for (const wrap of this.config?.wrap || []) {
|
|
220
267
|
await wrap.commit(trx, this.services);
|
|
221
268
|
}
|
|
222
269
|
return trx;
|
|
223
270
|
}
|
|
224
|
-
async
|
|
225
|
-
log_1.Log.error('module', this.module.name, `[${error.status}] ${error.toString()}`, error.stack);
|
|
271
|
+
async error(trx, error, idempotent) {
|
|
272
|
+
log_1.Log.error('module', this.module.name, `[${error.status}]${idempotent ? '*' : ''} ${error.toString()}`, error.stack);
|
|
226
273
|
trx_node_1.TrxNode.error(trx.root, error);
|
|
227
274
|
trx.end = datetime_1.NesoiDatetime.now();
|
|
275
|
+
const held_children = Object.keys(trx.holds).length;
|
|
276
|
+
if (held_children) {
|
|
277
|
+
log_1.Log.debug('module', this.module.name, `Rollback Holds ${(0, log_1.scopeTag)('trx', trx.root.globalId)} (${held_children})`);
|
|
278
|
+
await trx_1.Trx.rollbackHolds(trx);
|
|
279
|
+
}
|
|
228
280
|
if (idempotent)
|
|
229
281
|
return trx;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
282
|
+
return this.rollback(trx);
|
|
283
|
+
}
|
|
284
|
+
async rollback(trx) {
|
|
285
|
+
log_1.Log.warn('module', this.module.name, `Rollback ${(0, log_1.scopeTag)('trx', trx.root.globalId)} (held: ${Object.keys(trx.holds).length})`);
|
|
286
|
+
await this.log_adapter?.put(this.innerTrx.root, {
|
|
233
287
|
id: trx.id,
|
|
288
|
+
state: 'error',
|
|
234
289
|
origin: this.origin,
|
|
235
290
|
start: trx.start,
|
|
236
291
|
end: trx.end,
|
|
237
292
|
module: this.module.name,
|
|
238
293
|
});
|
|
294
|
+
await this.adapter.delete(this.innerTrx.root, trx.id);
|
|
239
295
|
for (const wrap of this.config?.wrap || []) {
|
|
240
296
|
await wrap.rollback(trx, this.services);
|
|
241
297
|
}
|
|
@@ -27,6 +27,9 @@ export type TrxNodeStatus = {
|
|
|
27
27
|
cached_buckets: number;
|
|
28
28
|
nodes: TrxNodeStatus[];
|
|
29
29
|
app: number;
|
|
30
|
+
ext?: {
|
|
31
|
+
idempotent: boolean;
|
|
32
|
+
};
|
|
30
33
|
};
|
|
31
34
|
/**
|
|
32
35
|
* @category Engine
|
|
@@ -53,7 +56,7 @@ export declare class TrxNode<Space extends $Space, M extends $Module, AuthUsers
|
|
|
53
56
|
constructor(scope: 'root' | `${string}::${TrxNodeBlock}:${string}` | `${string}::virtual`, trx: AnyTrx, parent: AnyTrxNode | undefined, module: AnyModule, auth?: {
|
|
54
57
|
tokens: AuthRequest<any>;
|
|
55
58
|
users: AuthUsers;
|
|
56
|
-
} | undefined, external?: boolean | undefined
|
|
59
|
+
} | undefined, external?: boolean | undefined);
|
|
57
60
|
static open(node: AnyTrxNode, action: string, input: Record<string, any>): void;
|
|
58
61
|
static hold(node: AnyTrxNode, output?: Record<string, any>): void;
|
|
59
62
|
static ok(node: AnyTrxNode, output?: Record<string, any>): void;
|
|
@@ -44,19 +44,16 @@ class TrxNode {
|
|
|
44
44
|
hold: undefined,
|
|
45
45
|
end: undefined
|
|
46
46
|
};
|
|
47
|
-
constructor(scope, trx, parent, module, auth, external
|
|
47
|
+
constructor(scope, trx, parent, module, auth, external) {
|
|
48
48
|
this.scope = scope;
|
|
49
49
|
this.trx = trx;
|
|
50
50
|
this.parent = parent;
|
|
51
51
|
this.module = module;
|
|
52
52
|
this.auth = auth;
|
|
53
53
|
this.external = external;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else {
|
|
58
|
-
this.id = '#' + (Math.random() + 1).toString(36).substring(7);
|
|
59
|
-
}
|
|
54
|
+
this.id = parent
|
|
55
|
+
? (Math.random() + 1).toString(36).substring(7)
|
|
56
|
+
: '#';
|
|
60
57
|
this.globalId = `${this.trx.id}.${this.id}`;
|
|
61
58
|
}
|
|
62
59
|
static open(node, action, input) {
|
|
@@ -201,7 +198,7 @@ class TrxNode {
|
|
|
201
198
|
// Status
|
|
202
199
|
status() {
|
|
203
200
|
return {
|
|
204
|
-
id: this.
|
|
201
|
+
id: this.globalId,
|
|
205
202
|
scope: this.scope,
|
|
206
203
|
state: this.state,
|
|
207
204
|
action: this.action,
|
|
@@ -210,7 +207,10 @@ class TrxNode {
|
|
|
210
207
|
error: this.error,
|
|
211
208
|
cached_buckets: Object.keys(this._cache).length,
|
|
212
209
|
nodes: this.children.map(child => child.status()),
|
|
213
|
-
app: this.time.end ? (this.time.end.epoch - this.time.start.epoch) : -1
|
|
210
|
+
app: this.time.end ? (this.time.end.epoch - this.time.start.epoch) : -1,
|
|
211
|
+
ext: this.action === '~' ? {
|
|
212
|
+
idempotent: this.input?.idempotent
|
|
213
|
+
} : undefined
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
//
|
|
@@ -219,6 +219,9 @@ class TrxNode {
|
|
|
219
219
|
to.children.push(child);
|
|
220
220
|
to.trx.addNode(child);
|
|
221
221
|
}
|
|
222
|
+
to.input = {
|
|
223
|
+
idempotent: from.trx.idempotent
|
|
224
|
+
};
|
|
222
225
|
}
|
|
223
226
|
static makeChildNode(node, module, block, name) {
|
|
224
227
|
const child = new TrxNode(`${module}::${block}:${name}`, node.trx, node, node.module, node.auth);
|