@arvo-tools/postgres 1.3.0 → 1.3.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/broker/index.js +157 -380
- package/dist/broker/index.js.map +1 -1
- package/dist/broker/utils.js +27 -54
- package/dist/broker/utils.js.map +1 -1
- package/dist/memory/factory/index.js +38 -100
- package/dist/memory/factory/index.js.map +1 -1
- package/dist/memory/v1/helper.js +37 -77
- package/dist/memory/v1/helper.js.map +1 -1
- package/dist/memory/v1/index.js +333 -574
- package/dist/memory/v1/index.js.map +1 -1
- package/dist/memory/v1/schema.js +62 -70
- package/dist/memory/v1/schema.js.map +1 -1
- package/package.json +1 -1
package/dist/memory/v1/index.js
CHANGED
|
@@ -1,62 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
-
function step(op) {
|
|
27
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
-
switch (op[0]) {
|
|
32
|
-
case 0: case 1: t = op; break;
|
|
33
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
-
default:
|
|
37
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
-
if (t[2]) _.ops.pop();
|
|
42
|
-
_.trys.pop(); continue;
|
|
43
|
-
}
|
|
44
|
-
op = body.call(thisArg, _);
|
|
45
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
4
|
};
|
|
52
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
6
|
exports.PostgressMachineMemoryV1 = void 0;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
7
|
+
const api_1 = require("@opentelemetry/api");
|
|
8
|
+
const arvo_core_1 = require("arvo-core");
|
|
9
|
+
const arvo_event_handler_1 = require("arvo-event-handler");
|
|
10
|
+
const pg_1 = require("pg");
|
|
11
|
+
const pg_format_1 = __importDefault(require("pg-format"));
|
|
12
|
+
const schema_1 = require("./schema");
|
|
60
13
|
/**
|
|
61
14
|
* PostgreSQL-backed implementation of IMachineMemory for distributed workflow state management.
|
|
62
15
|
*
|
|
@@ -72,455 +25,302 @@ var schema_1 = require("./schema");
|
|
|
72
25
|
* - Lock table: Manages distributed locks with automatic expiration
|
|
73
26
|
* - Hierarchy table: Tracks workflow parent-child relationships and root subjects
|
|
74
27
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
28
|
+
class PostgressMachineMemoryV1 {
|
|
29
|
+
dbSchemaName;
|
|
30
|
+
tables;
|
|
31
|
+
lockConfig;
|
|
32
|
+
enableCleanup;
|
|
33
|
+
enableOtel;
|
|
34
|
+
pool;
|
|
35
|
+
constructor(param) {
|
|
78
36
|
this.dbSchemaName = param.schema;
|
|
79
37
|
this.tables = param.tables;
|
|
80
38
|
this.lockConfig = {
|
|
81
|
-
maxRetries:
|
|
82
|
-
initialDelayMs:
|
|
83
|
-
backoffExponent:
|
|
84
|
-
ttlMs:
|
|
39
|
+
maxRetries: param.config?.lockConfig?.maxRetries ?? 3,
|
|
40
|
+
initialDelayMs: param.config?.lockConfig?.initialDelayMs ?? 100,
|
|
41
|
+
backoffExponent: param.config?.lockConfig?.backoffExponent ?? 1.5,
|
|
42
|
+
ttlMs: param.config?.lockConfig?.ttlMs ?? 120000,
|
|
85
43
|
};
|
|
86
|
-
this.enableCleanup =
|
|
87
|
-
this.enableOtel =
|
|
88
|
-
|
|
44
|
+
this.enableCleanup = param.config?.enableCleanup ?? false;
|
|
45
|
+
this.enableOtel = param.config?.enableOtel ?? false;
|
|
46
|
+
let poolConfig;
|
|
89
47
|
if (param.config && 'connectionString' in param.config) {
|
|
90
48
|
poolConfig = {
|
|
91
49
|
connectionString: param.config.connectionString,
|
|
92
|
-
max:
|
|
93
|
-
idleTimeoutMillis:
|
|
94
|
-
connectionTimeoutMillis:
|
|
95
|
-
statement_timeout:
|
|
96
|
-
query_timeout:
|
|
50
|
+
max: param.config.max ?? 10,
|
|
51
|
+
idleTimeoutMillis: param.config.idleTimeoutMillis ?? 30000,
|
|
52
|
+
connectionTimeoutMillis: param.config.connectionTimeoutMillis ?? 5000,
|
|
53
|
+
statement_timeout: param.config.statementTimeoutMillis ?? 30000,
|
|
54
|
+
query_timeout: param.config.queryTimeoutMillis ?? 30000,
|
|
97
55
|
};
|
|
98
56
|
}
|
|
99
57
|
else {
|
|
100
|
-
|
|
58
|
+
const cfg = param.config;
|
|
101
59
|
poolConfig = {
|
|
102
|
-
host:
|
|
103
|
-
port:
|
|
104
|
-
user:
|
|
105
|
-
password:
|
|
106
|
-
database:
|
|
107
|
-
max:
|
|
108
|
-
idleTimeoutMillis:
|
|
109
|
-
connectionTimeoutMillis:
|
|
110
|
-
statement_timeout:
|
|
111
|
-
query_timeout:
|
|
60
|
+
host: cfg?.host ?? 'localhost',
|
|
61
|
+
port: cfg?.port ?? 5432,
|
|
62
|
+
user: cfg?.user ?? 'postgres',
|
|
63
|
+
password: cfg?.password ?? 'postgres',
|
|
64
|
+
database: cfg?.database ?? 'postgres',
|
|
65
|
+
max: cfg?.max ?? 10,
|
|
66
|
+
idleTimeoutMillis: cfg?.idleTimeoutMillis ?? 30000,
|
|
67
|
+
connectionTimeoutMillis: cfg?.connectionTimeoutMillis ?? 5000,
|
|
68
|
+
statement_timeout: cfg?.statementTimeoutMillis ?? 30000,
|
|
69
|
+
query_timeout: cfg?.queryTimeoutMillis ?? 30000,
|
|
112
70
|
};
|
|
113
71
|
}
|
|
114
72
|
this.pool = new pg_1.Pool(poolConfig);
|
|
115
73
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
74
|
+
async close() {
|
|
75
|
+
await this.pool.end();
|
|
76
|
+
}
|
|
77
|
+
async delay(ms) {
|
|
78
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
}
|
|
80
|
+
async validateTableStructure() {
|
|
81
|
+
const client = await this.pool.connect();
|
|
82
|
+
try {
|
|
83
|
+
await Promise.all([
|
|
84
|
+
(0, schema_1.validateTable)(client, this.dbSchemaName, this.tables.state, 'state'),
|
|
85
|
+
(0, schema_1.validateTable)(client, this.dbSchemaName, this.tables.lock, 'lock'),
|
|
86
|
+
(0, schema_1.validateTable)(client, this.dbSchemaName, this.tables.hierarchy, 'hierarchy'),
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
client.release();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async otel({ name, fn }) {
|
|
94
|
+
if (!this.enableOtel) {
|
|
95
|
+
return await fn();
|
|
96
|
+
}
|
|
97
|
+
return await arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
98
|
+
name: name,
|
|
99
|
+
disableSpanManagement: true,
|
|
100
|
+
fn,
|
|
133
101
|
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return [3 /*break*/, 5];
|
|
154
|
-
case 4:
|
|
155
|
-
client.release();
|
|
156
|
-
return [7 /*endfinally*/];
|
|
157
|
-
case 5: return [2 /*return*/];
|
|
102
|
+
}
|
|
103
|
+
async read(id) {
|
|
104
|
+
return await this.otel({
|
|
105
|
+
name: 'PostgresMachineMemory.v1.read',
|
|
106
|
+
fn: async (span) => {
|
|
107
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
108
|
+
span?.setAttribute('subject', id);
|
|
109
|
+
const client = await this.pool.connect();
|
|
110
|
+
try {
|
|
111
|
+
const result = await client.query((0, pg_format_1.default)('SELECT data, version FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.state), [id]);
|
|
112
|
+
if (!result.rows.length) {
|
|
113
|
+
span?.setAttribute('available', 0);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
span?.setAttribute('available', 1);
|
|
117
|
+
return {
|
|
118
|
+
...(result.rows[0].data ?? {}),
|
|
119
|
+
__postgres_version_counter_data_$$__: result.rows[0].version,
|
|
120
|
+
};
|
|
158
121
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return __generator(this, function (_c) {
|
|
166
|
-
switch (_c.label) {
|
|
167
|
-
case 0:
|
|
168
|
-
if (!!this.enableOtel) return [3 /*break*/, 2];
|
|
169
|
-
return [4 /*yield*/, fn()];
|
|
170
|
-
case 1: return [2 /*return*/, _c.sent()];
|
|
171
|
-
case 2: return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
172
|
-
name: name,
|
|
173
|
-
disableSpanManagement: true,
|
|
174
|
-
fn: fn,
|
|
175
|
-
})];
|
|
176
|
-
case 3: return [2 /*return*/, _c.sent()];
|
|
122
|
+
catch (error) {
|
|
123
|
+
span?.setStatus({
|
|
124
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
125
|
+
message: error.message,
|
|
126
|
+
});
|
|
127
|
+
throw error;
|
|
177
128
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
PostgressMachineMemoryV1.prototype.read = function (id) {
|
|
182
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
183
|
-
var _this = this;
|
|
184
|
-
return __generator(this, function (_a) {
|
|
185
|
-
switch (_a.label) {
|
|
186
|
-
case 0: return [4 /*yield*/, this.otel({
|
|
187
|
-
name: 'PostgresMachineMemory.v1.read',
|
|
188
|
-
fn: function (span) { return __awaiter(_this, void 0, void 0, function () {
|
|
189
|
-
var client, result, error_1;
|
|
190
|
-
var _a;
|
|
191
|
-
return __generator(this, function (_b) {
|
|
192
|
-
switch (_b.label) {
|
|
193
|
-
case 0:
|
|
194
|
-
span === null || span === void 0 ? void 0 : span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
195
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('subject', id);
|
|
196
|
-
return [4 /*yield*/, this.pool.connect()];
|
|
197
|
-
case 1:
|
|
198
|
-
client = _b.sent();
|
|
199
|
-
_b.label = 2;
|
|
200
|
-
case 2:
|
|
201
|
-
_b.trys.push([2, 4, 5, 6]);
|
|
202
|
-
return [4 /*yield*/, client.query((0, pg_format_1.default)('SELECT data, version FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.state), [id])];
|
|
203
|
-
case 3:
|
|
204
|
-
result = _b.sent();
|
|
205
|
-
if (!result.rows.length) {
|
|
206
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('available', 0);
|
|
207
|
-
return [2 /*return*/, null];
|
|
208
|
-
}
|
|
209
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('available', 1);
|
|
210
|
-
return [2 /*return*/, __assign(__assign({}, ((_a = result.rows[0].data) !== null && _a !== void 0 ? _a : {})), { __postgres_version_counter_data_$$__: result.rows[0].version })];
|
|
211
|
-
case 4:
|
|
212
|
-
error_1 = _b.sent();
|
|
213
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
214
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
215
|
-
message: error_1.message,
|
|
216
|
-
});
|
|
217
|
-
throw error_1;
|
|
218
|
-
case 5:
|
|
219
|
-
client.release();
|
|
220
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
221
|
-
return [7 /*endfinally*/];
|
|
222
|
-
case 6: return [2 /*return*/];
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}); },
|
|
226
|
-
})];
|
|
227
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
129
|
+
finally {
|
|
130
|
+
client.release();
|
|
131
|
+
span?.end();
|
|
228
132
|
}
|
|
229
|
-
}
|
|
133
|
+
},
|
|
230
134
|
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
_e.sent();
|
|
296
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
297
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
298
|
-
message: error_2.message,
|
|
299
|
-
});
|
|
300
|
-
throw error_2;
|
|
301
|
-
case 13:
|
|
302
|
-
currentVersion = prevData.__postgres_version_counter_data_$$__;
|
|
303
|
-
newVersion = currentVersion + 1;
|
|
304
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('version', newVersion);
|
|
305
|
-
return [4 /*yield*/, client.query((0, pg_format_1.default)('UPDATE %I.%I SET data = $1, version = $2, execution_status = $3, updated_at = NOW() WHERE subject = $4 AND version = $5', this.dbSchemaName, this.tables.state), [JSON.stringify(data), newVersion, resolvedExectionStatus, id, currentVersion])];
|
|
306
|
-
case 14:
|
|
307
|
-
result = _e.sent();
|
|
308
|
-
if (result.rowCount === 0) {
|
|
309
|
-
error = new Error("Data is corrupted due to version mismatch for subject '".concat(id, "'. Expected version ").concat(currentVersion, " but state has been modified."));
|
|
310
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
311
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
312
|
-
message: error.message,
|
|
313
|
-
});
|
|
314
|
-
throw error;
|
|
315
|
-
}
|
|
316
|
-
return [3 /*break*/, 17];
|
|
317
|
-
case 15:
|
|
318
|
-
error_3 = _e.sent();
|
|
319
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
320
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
321
|
-
message: error_3.message,
|
|
322
|
-
});
|
|
323
|
-
throw error_3;
|
|
324
|
-
case 16:
|
|
325
|
-
client.release();
|
|
326
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
327
|
-
return [7 /*endfinally*/];
|
|
328
|
-
case 17: return [2 /*return*/];
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
}); },
|
|
332
|
-
})];
|
|
333
|
-
case 1: return [2 /*return*/, _c.sent()];
|
|
135
|
+
}
|
|
136
|
+
async write(id, data, prevData, { source, initiator }) {
|
|
137
|
+
return await this.otel({
|
|
138
|
+
name: 'PostgresMachineMemory.v1.write',
|
|
139
|
+
fn: async (span) => {
|
|
140
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
141
|
+
span?.setAttribute('subject', id);
|
|
142
|
+
span?.setAttribute('isNew', prevData === null ? 1 : 0);
|
|
143
|
+
const client = await this.pool.connect();
|
|
144
|
+
const resolvedExectionStatus = data.executionStatus ?? 'unknown';
|
|
145
|
+
try {
|
|
146
|
+
if (prevData === null) {
|
|
147
|
+
try {
|
|
148
|
+
await client.query('BEGIN');
|
|
149
|
+
const resolvedParentSubject = data.parentSubject ?? null;
|
|
150
|
+
const resolvedInitiator = arvo_event_handler_1.Materialized.isResolved(initiator) ? initiator.value : null;
|
|
151
|
+
await client.query((0, pg_format_1.default)('INSERT INTO %I.%I (subject, data, version, execution_status, parent_subject, initiator, source, created_at, updated_at) VALUES ($1, $2, 1, $3, $4, $5, $6, NOW(), NOW())', this.dbSchemaName, this.tables.state), [
|
|
152
|
+
id,
|
|
153
|
+
JSON.stringify(data),
|
|
154
|
+
resolvedExectionStatus,
|
|
155
|
+
resolvedParentSubject,
|
|
156
|
+
resolvedInitiator,
|
|
157
|
+
source,
|
|
158
|
+
]);
|
|
159
|
+
let rootSubject;
|
|
160
|
+
if (resolvedParentSubject === null) {
|
|
161
|
+
rootSubject = id;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const parentResult = await client.query((0, pg_format_1.default)('SELECT root_subject FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.hierarchy), [resolvedParentSubject]);
|
|
165
|
+
rootSubject = parentResult.rows[0]?.root_subject ?? id;
|
|
166
|
+
}
|
|
167
|
+
await client.query((0, pg_format_1.default)('INSERT INTO %I.%I (subject, parent_subject, root_subject, created_at) VALUES ($1, $2, $3, NOW())', this.dbSchemaName, this.tables.hierarchy), [id, resolvedParentSubject, rootSubject]);
|
|
168
|
+
await client.query('COMMIT');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
await client.query('ROLLBACK');
|
|
173
|
+
span?.setStatus({
|
|
174
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
175
|
+
message: error.message,
|
|
176
|
+
});
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const currentVersion = prevData.__postgres_version_counter_data_$$__;
|
|
181
|
+
const newVersion = currentVersion + 1;
|
|
182
|
+
span?.setAttribute('version', newVersion);
|
|
183
|
+
const result = await client.query((0, pg_format_1.default)('UPDATE %I.%I SET data = $1, version = $2, execution_status = $3, updated_at = NOW() WHERE subject = $4 AND version = $5', this.dbSchemaName, this.tables.state), [JSON.stringify(data), newVersion, resolvedExectionStatus, id, currentVersion]);
|
|
184
|
+
if (result.rowCount === 0) {
|
|
185
|
+
const error = new Error(`Data is corrupted due to version mismatch for subject '${id}'. Expected version ${currentVersion} but state has been modified.`);
|
|
186
|
+
span?.setStatus({
|
|
187
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
188
|
+
message: error.message,
|
|
189
|
+
});
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
span?.setStatus({
|
|
195
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
196
|
+
message: error.message,
|
|
197
|
+
});
|
|
198
|
+
throw error;
|
|
334
199
|
}
|
|
335
|
-
|
|
200
|
+
finally {
|
|
201
|
+
client.release();
|
|
202
|
+
span?.end();
|
|
203
|
+
}
|
|
204
|
+
},
|
|
336
205
|
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
});
|
|
386
|
-
throw error_4;
|
|
387
|
-
case 9:
|
|
388
|
-
attempt++;
|
|
389
|
-
return [3 /*break*/, 3];
|
|
390
|
-
case 10:
|
|
391
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('acquired', 0);
|
|
392
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('attempts', this.lockConfig.maxRetries + 1);
|
|
393
|
-
return [2 /*return*/, false];
|
|
394
|
-
case 11:
|
|
395
|
-
error_5 = _a.sent();
|
|
396
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
397
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
398
|
-
message: error_5.message,
|
|
399
|
-
});
|
|
400
|
-
throw error_5;
|
|
401
|
-
case 12:
|
|
402
|
-
client.release();
|
|
403
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
404
|
-
return [7 /*endfinally*/];
|
|
405
|
-
case 13: return [2 /*return*/];
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}); },
|
|
409
|
-
})];
|
|
410
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
206
|
+
}
|
|
207
|
+
async lock(id) {
|
|
208
|
+
return await this.otel({
|
|
209
|
+
name: 'PostgresMachineMemory.v1.lock',
|
|
210
|
+
fn: async (span) => {
|
|
211
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
212
|
+
span?.setAttribute('subject', id);
|
|
213
|
+
const client = await this.pool.connect();
|
|
214
|
+
try {
|
|
215
|
+
for (let attempt = 0; attempt <= this.lockConfig.maxRetries; attempt++) {
|
|
216
|
+
try {
|
|
217
|
+
const result = await client.query((0, pg_format_1.default)(`WITH arvo_lock_time AS (SELECT NOW() as now)
|
|
218
|
+
INSERT INTO %I.%I (subject, locked_at, expires_at, created_at)
|
|
219
|
+
SELECT $1, now, now + ($2 || ' milliseconds')::INTERVAL, now FROM arvo_lock_time
|
|
220
|
+
ON CONFLICT (subject)
|
|
221
|
+
DO UPDATE SET
|
|
222
|
+
locked_at = (SELECT now FROM arvo_lock_time),
|
|
223
|
+
expires_at = (SELECT now FROM arvo_lock_time) + ($2 || ' milliseconds')::INTERVAL
|
|
224
|
+
WHERE %I.%I.expires_at < (SELECT now FROM arvo_lock_time)
|
|
225
|
+
RETURNING subject`, this.dbSchemaName, this.tables.lock, this.dbSchemaName, this.tables.lock), [id, this.lockConfig.ttlMs]);
|
|
226
|
+
if (result.rowCount && result.rowCount > 0) {
|
|
227
|
+
span?.setAttribute('acquired', 1);
|
|
228
|
+
span?.setAttribute('attempts', attempt + 1);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (attempt < this.lockConfig.maxRetries) {
|
|
232
|
+
const delayMs = this.lockConfig.initialDelayMs * this.lockConfig.backoffExponent ** attempt;
|
|
233
|
+
await this.delay(delayMs);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
span?.setStatus({
|
|
238
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
239
|
+
message: error.message,
|
|
240
|
+
});
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
span?.setAttribute('acquired', 0);
|
|
245
|
+
span?.setAttribute('attempts', this.lockConfig.maxRetries + 1);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
span?.setStatus({
|
|
250
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
251
|
+
message: error.message,
|
|
252
|
+
});
|
|
253
|
+
throw error;
|
|
411
254
|
}
|
|
412
|
-
|
|
255
|
+
finally {
|
|
256
|
+
client.release();
|
|
257
|
+
span?.end();
|
|
258
|
+
}
|
|
259
|
+
},
|
|
413
260
|
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
switch (_a.label) {
|
|
426
|
-
case 0:
|
|
427
|
-
span === null || span === void 0 ? void 0 : span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
428
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('subject', id);
|
|
429
|
-
return [4 /*yield*/, this.pool.connect()];
|
|
430
|
-
case 1:
|
|
431
|
-
client = _a.sent();
|
|
432
|
-
_a.label = 2;
|
|
433
|
-
case 2:
|
|
434
|
-
_a.trys.push([2, 4, 5, 6]);
|
|
435
|
-
return [4 /*yield*/, client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.lock), [id])];
|
|
436
|
-
case 3:
|
|
437
|
-
_a.sent();
|
|
438
|
-
return [2 /*return*/, true];
|
|
439
|
-
case 4:
|
|
440
|
-
error_6 = _a.sent();
|
|
441
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
442
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
443
|
-
message: error_6.message,
|
|
444
|
-
});
|
|
445
|
-
return [2 /*return*/, true];
|
|
446
|
-
case 5:
|
|
447
|
-
client.release();
|
|
448
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
449
|
-
return [7 /*endfinally*/];
|
|
450
|
-
case 6: return [2 /*return*/];
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
}); },
|
|
454
|
-
})];
|
|
455
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
261
|
+
}
|
|
262
|
+
async unlock(id) {
|
|
263
|
+
return await this.otel({
|
|
264
|
+
name: 'PostgresMachineMemory.v1.unlock',
|
|
265
|
+
fn: async (span) => {
|
|
266
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
267
|
+
span?.setAttribute('subject', id);
|
|
268
|
+
const client = await this.pool.connect();
|
|
269
|
+
try {
|
|
270
|
+
await client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.lock), [id]);
|
|
271
|
+
return true;
|
|
456
272
|
}
|
|
457
|
-
|
|
273
|
+
catch (error) {
|
|
274
|
+
span?.setStatus({
|
|
275
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
276
|
+
message: error.message,
|
|
277
|
+
});
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
client.release();
|
|
282
|
+
span?.end();
|
|
283
|
+
}
|
|
284
|
+
},
|
|
458
285
|
});
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
case 1:
|
|
482
|
-
client = _a.sent();
|
|
483
|
-
_a.label = 2;
|
|
484
|
-
case 2:
|
|
485
|
-
_a.trys.push([2, 6, 8, 9]);
|
|
486
|
-
return [4 /*yield*/, client.query('BEGIN')];
|
|
487
|
-
case 3:
|
|
488
|
-
_a.sent();
|
|
489
|
-
return [4 /*yield*/, Promise.all([
|
|
490
|
-
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.state), [id]),
|
|
491
|
-
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.lock), [id]),
|
|
492
|
-
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.hierarchy), [id]),
|
|
493
|
-
])];
|
|
494
|
-
case 4:
|
|
495
|
-
_a.sent();
|
|
496
|
-
return [4 /*yield*/, client.query('COMMIT')];
|
|
497
|
-
case 5:
|
|
498
|
-
_a.sent();
|
|
499
|
-
return [3 /*break*/, 9];
|
|
500
|
-
case 6:
|
|
501
|
-
error_7 = _a.sent();
|
|
502
|
-
return [4 /*yield*/, client.query('ROLLBACK')];
|
|
503
|
-
case 7:
|
|
504
|
-
_a.sent();
|
|
505
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
506
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
507
|
-
message: error_7.message,
|
|
508
|
-
});
|
|
509
|
-
throw error_7;
|
|
510
|
-
case 8:
|
|
511
|
-
client.release();
|
|
512
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
513
|
-
return [7 /*endfinally*/];
|
|
514
|
-
case 9: return [2 /*return*/];
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
|
-
}); },
|
|
518
|
-
})];
|
|
519
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
286
|
+
}
|
|
287
|
+
async cleanup(id) {
|
|
288
|
+
return await this.otel({
|
|
289
|
+
name: 'PostgresMachineMemory.v1.cleanup',
|
|
290
|
+
fn: async (span) => {
|
|
291
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
292
|
+
span?.setAttribute('subject', id);
|
|
293
|
+
if (!this.enableCleanup) {
|
|
294
|
+
span?.setAttribute('skipped', 1);
|
|
295
|
+
span?.end();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
span?.setAttribute('skipped', 0);
|
|
299
|
+
const client = await this.pool.connect();
|
|
300
|
+
try {
|
|
301
|
+
await client.query('BEGIN');
|
|
302
|
+
await Promise.all([
|
|
303
|
+
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.state), [id]),
|
|
304
|
+
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.lock), [id]),
|
|
305
|
+
client.query((0, pg_format_1.default)('DELETE FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.hierarchy), [id]),
|
|
306
|
+
]);
|
|
307
|
+
await client.query('COMMIT');
|
|
520
308
|
}
|
|
521
|
-
|
|
309
|
+
catch (error) {
|
|
310
|
+
await client.query('ROLLBACK');
|
|
311
|
+
span?.setStatus({
|
|
312
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
313
|
+
message: error.message,
|
|
314
|
+
});
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
client.release();
|
|
319
|
+
span?.end();
|
|
320
|
+
}
|
|
321
|
+
},
|
|
522
322
|
});
|
|
523
|
-
}
|
|
323
|
+
}
|
|
524
324
|
/**
|
|
525
325
|
* Retrieves all child workflow subjects belonging to a specific root workflow.
|
|
526
326
|
*
|
|
@@ -537,55 +337,35 @@ var PostgressMachineMemoryV1 = /** @class */ (function () {
|
|
|
537
337
|
* console.log(`Found ${children.length} child workflows subjects`);
|
|
538
338
|
* ```
|
|
539
339
|
*/
|
|
540
|
-
|
|
541
|
-
return
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
.filter(function (item) { return item !== rootSubject; });
|
|
566
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('count', subjects.length);
|
|
567
|
-
return [2 /*return*/, subjects];
|
|
568
|
-
case 4:
|
|
569
|
-
error_8 = _a.sent();
|
|
570
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
571
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
572
|
-
message: error_8.message,
|
|
573
|
-
});
|
|
574
|
-
throw error_8;
|
|
575
|
-
case 5:
|
|
576
|
-
client.release();
|
|
577
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
578
|
-
return [7 /*endfinally*/];
|
|
579
|
-
case 6: return [2 /*return*/];
|
|
580
|
-
}
|
|
581
|
-
});
|
|
582
|
-
}); },
|
|
583
|
-
})];
|
|
584
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
340
|
+
async getSubjectsByRoot(rootSubject) {
|
|
341
|
+
return await this.otel({
|
|
342
|
+
name: 'PostgresMachineMemory.v1.getSubjectsByRoot',
|
|
343
|
+
fn: async (span) => {
|
|
344
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
345
|
+
span?.setAttribute('rootSubject', rootSubject);
|
|
346
|
+
const client = await this.pool.connect();
|
|
347
|
+
try {
|
|
348
|
+
const result = await client.query((0, pg_format_1.default)('SELECT subject FROM %I.%I WHERE root_subject = $1', this.dbSchemaName, this.tables.hierarchy), [rootSubject]);
|
|
349
|
+
const subjects = result.rows
|
|
350
|
+
.map((row) => row.subject)
|
|
351
|
+
.filter((item) => item !== rootSubject);
|
|
352
|
+
span?.setAttribute('count', subjects.length);
|
|
353
|
+
return subjects;
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
span?.setStatus({
|
|
357
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
358
|
+
message: error.message,
|
|
359
|
+
});
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
client.release();
|
|
364
|
+
span?.end();
|
|
585
365
|
}
|
|
586
|
-
}
|
|
366
|
+
},
|
|
587
367
|
});
|
|
588
|
-
}
|
|
368
|
+
}
|
|
589
369
|
/**
|
|
590
370
|
* Retrieves the root workflow subject for a given workflow instance.
|
|
591
371
|
*
|
|
@@ -613,60 +393,39 @@ var PostgressMachineMemoryV1 = /** @class */ (function () {
|
|
|
613
393
|
* }
|
|
614
394
|
* ```
|
|
615
395
|
*/
|
|
616
|
-
|
|
617
|
-
return
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return [2 /*return*/, null];
|
|
642
|
-
}
|
|
643
|
-
rootSubject = result.rows[0].root_subject;
|
|
644
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('found', 1);
|
|
645
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('isRoot', 0);
|
|
646
|
-
span === null || span === void 0 ? void 0 : span.setAttribute('rootSubject', rootSubject);
|
|
647
|
-
return [2 /*return*/, rootSubject];
|
|
648
|
-
case 4:
|
|
649
|
-
error_9 = _a.sent();
|
|
650
|
-
span === null || span === void 0 ? void 0 : span.setStatus({
|
|
651
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
652
|
-
message: error_9.message,
|
|
653
|
-
});
|
|
654
|
-
throw error_9;
|
|
655
|
-
case 5:
|
|
656
|
-
client.release();
|
|
657
|
-
span === null || span === void 0 ? void 0 : span.end();
|
|
658
|
-
return [7 /*endfinally*/];
|
|
659
|
-
case 6: return [2 /*return*/];
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
}); },
|
|
663
|
-
})];
|
|
664
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
396
|
+
async getRootSubject(subject) {
|
|
397
|
+
return await this.otel({
|
|
398
|
+
name: 'PostgresMachineMemory.v1.getRootSubject',
|
|
399
|
+
fn: async (span) => {
|
|
400
|
+
span?.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
401
|
+
span?.setAttribute('subject', subject);
|
|
402
|
+
const client = await this.pool.connect();
|
|
403
|
+
try {
|
|
404
|
+
const result = await client.query((0, pg_format_1.default)('SELECT root_subject FROM %I.%I WHERE subject = $1', this.dbSchemaName, this.tables.hierarchy), [subject]);
|
|
405
|
+
if (!result.rows.length) {
|
|
406
|
+
span?.setAttribute('found', 0);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const rootSubject = result.rows[0].root_subject;
|
|
410
|
+
span?.setAttribute('found', 1);
|
|
411
|
+
span?.setAttribute('isRoot', 0);
|
|
412
|
+
span?.setAttribute('rootSubject', rootSubject);
|
|
413
|
+
return rootSubject;
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
span?.setStatus({
|
|
417
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
418
|
+
message: error.message,
|
|
419
|
+
});
|
|
420
|
+
throw error;
|
|
665
421
|
}
|
|
666
|
-
|
|
422
|
+
finally {
|
|
423
|
+
client.release();
|
|
424
|
+
span?.end();
|
|
425
|
+
}
|
|
426
|
+
},
|
|
667
427
|
});
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
}());
|
|
428
|
+
}
|
|
429
|
+
}
|
|
671
430
|
exports.PostgressMachineMemoryV1 = PostgressMachineMemoryV1;
|
|
672
431
|
//# sourceMappingURL=index.js.map
|