@ductape/sdk 0.4.4 → 0.4.5
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/context/execution-context.d.ts +6 -0
- package/dist/context/execution-context.js +1 -1
- package/dist/context/execution-context.js.map +1 -1
- package/dist/database/adapters/dynamodb.adapter.d.ts +2 -0
- package/dist/database/adapters/dynamodb.adapter.js +18 -1
- package/dist/database/adapters/dynamodb.adapter.js.map +1 -1
- package/dist/database/adapters/mongodb.adapter.d.ts +2 -0
- package/dist/database/adapters/mongodb.adapter.js +31 -8
- package/dist/database/adapters/mongodb.adapter.js.map +1 -1
- package/dist/database/adapters/mysql.adapter.d.ts +2 -0
- package/dist/database/adapters/mysql.adapter.js +89 -14
- package/dist/database/adapters/mysql.adapter.js.map +1 -1
- package/dist/database/adapters/postgresql.adapter.d.ts +2 -0
- package/dist/database/adapters/postgresql.adapter.js +23 -3
- package/dist/database/adapters/postgresql.adapter.js.map +1 -1
- package/dist/database/atomic-batch.d.ts +51 -0
- package/dist/database/atomic-batch.js +174 -0
- package/dist/database/atomic-batch.js.map +1 -0
- package/dist/database/databases.service.d.ts +4 -0
- package/dist/database/databases.service.js +116 -3
- package/dist/database/databases.service.js.map +1 -1
- package/dist/database/dynamodb-atomic-batch.d.ts +8 -0
- package/dist/database/dynamodb-atomic-batch.js +153 -0
- package/dist/database/dynamodb-atomic-batch.js.map +1 -0
- package/dist/database/index.d.ts +1 -0
- package/dist/database/index.js +4 -2
- package/dist/database/index.js.map +1 -1
- package/dist/database/transactions/transaction-manager.d.ts +5 -1
- package/dist/database/transactions/transaction-manager.js +13 -2
- package/dist/database/transactions/transaction-manager.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/logs/logs.service.js +10 -0
- package/dist/logs/logs.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ITransaction } from './types/transaction.interface';
|
|
2
|
+
export interface AtomicBatchOperation {
|
|
3
|
+
name: string;
|
|
4
|
+
operation: 'insert' | 'update' | 'delete' | 'upsert';
|
|
5
|
+
table: string;
|
|
6
|
+
data?: Record<string, unknown> | Record<string, unknown>[];
|
|
7
|
+
where?: Record<string, unknown>;
|
|
8
|
+
conflictKeys?: string[];
|
|
9
|
+
returning?: boolean;
|
|
10
|
+
/** Exact primitive count, not matched count. MongoDB update counts modified documents. */
|
|
11
|
+
expectedCount?: number;
|
|
12
|
+
/** Output label -> JSON pointer into this operation's actual primitive result. */
|
|
13
|
+
project?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface AtomicBatchOptions {
|
|
16
|
+
product: string;
|
|
17
|
+
env: string;
|
|
18
|
+
database: string;
|
|
19
|
+
/** Original opaque session, forwarded unchanged to every operation. */
|
|
20
|
+
session?: string;
|
|
21
|
+
/** Explicit maintenance classification; does not grant or bypass access. */
|
|
22
|
+
system?: {
|
|
23
|
+
reason: string;
|
|
24
|
+
};
|
|
25
|
+
transaction?: ITransaction;
|
|
26
|
+
operations: AtomicBatchOperation[];
|
|
27
|
+
}
|
|
28
|
+
export interface AtomicBatchResult {
|
|
29
|
+
status: 'committed' | 'pending';
|
|
30
|
+
transactionId: string;
|
|
31
|
+
results: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
export declare class AtomicBatchError extends Error {
|
|
34
|
+
readonly code: 'BATCH_VALIDATION' | 'BATCH_GUARD_FAILED' | 'BATCH_OPERATION_FAILED' | 'BATCH_COMMIT_UNKNOWN';
|
|
35
|
+
readonly outcome: 'not_started' | 'aborted' | 'rollback_unknown' | 'commit_unknown';
|
|
36
|
+
readonly operation?: string;
|
|
37
|
+
readonly transactionId?: string;
|
|
38
|
+
constructor(code: 'BATCH_VALIDATION' | 'BATCH_GUARD_FAILED' | 'BATCH_OPERATION_FAILED' | 'BATCH_COMMIT_UNKNOWN', outcome: 'not_started' | 'aborted' | 'rollback_unknown' | 'commit_unknown', operation?: string, transactionId?: string);
|
|
39
|
+
}
|
|
40
|
+
export interface AtomicBatchHost {
|
|
41
|
+
preflight(options: AtomicBatchOptions): Promise<void>;
|
|
42
|
+
executeNative?(options: AtomicBatchOptions): Promise<AtomicBatchResult | undefined>;
|
|
43
|
+
begin(options: AtomicBatchOptions): Promise<ITransaction>;
|
|
44
|
+
validateTransaction?(options: AtomicBatchOptions, transaction: ITransaction): Promise<void>;
|
|
45
|
+
execute(operation: AtomicBatchOperation, options: AtomicBatchOptions, transaction: ITransaction): Promise<{
|
|
46
|
+
count: number;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare function validateAtomicBatch(options: AtomicBatchOptions): void;
|
|
50
|
+
/** Sequential ACID executor. No automatic write replay and no provider callbacks. */
|
|
51
|
+
export declare function executeAtomicBatch(host: AtomicBatchHost, options: AtomicBatchOptions): Promise<AtomicBatchResult>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AtomicBatchError = void 0;
|
|
4
|
+
exports.validateAtomicBatch = validateAtomicBatch;
|
|
5
|
+
exports.executeAtomicBatch = executeAtomicBatch;
|
|
6
|
+
class AtomicBatchError extends Error {
|
|
7
|
+
constructor(code, outcome, operation, transactionId) {
|
|
8
|
+
// Never copy provider errors here: they can contain parameters and credentials.
|
|
9
|
+
super(`${code}: ${outcome}${operation ? ` (${operation})` : ''}`);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.outcome = outcome;
|
|
12
|
+
this.operation = operation;
|
|
13
|
+
this.transactionId = transactionId;
|
|
14
|
+
this.name = 'AtomicBatchError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.AtomicBatchError = AtomicBatchError;
|
|
18
|
+
const forbidden = new Set(['__proto__', 'constructor', 'prototype']);
|
|
19
|
+
function fail() { throw new AtomicBatchError('BATCH_VALIDATION', 'not_started'); }
|
|
20
|
+
function record(value) {
|
|
21
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
22
|
+
&& (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
23
|
+
}
|
|
24
|
+
function keys(value, allowed) {
|
|
25
|
+
if (Object.keys(value).some(key => !allowed.includes(key) || forbidden.has(key)))
|
|
26
|
+
fail();
|
|
27
|
+
}
|
|
28
|
+
function pointer(value, path) {
|
|
29
|
+
if (path === '')
|
|
30
|
+
return value;
|
|
31
|
+
if (!path.startsWith('/') || /~(?![01])/u.test(path))
|
|
32
|
+
fail();
|
|
33
|
+
for (const encoded of path.slice(1).split('/')) {
|
|
34
|
+
const key = encoded.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
35
|
+
if (forbidden.has(key) || value === null || typeof value !== 'object' || !Object.prototype.hasOwnProperty.call(value, key))
|
|
36
|
+
fail();
|
|
37
|
+
value = value[key];
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function walk(value, visit, depth = 0) {
|
|
42
|
+
if (depth > 32)
|
|
43
|
+
fail();
|
|
44
|
+
if (Array.isArray(value))
|
|
45
|
+
return value.map(item => walk(item, visit, depth + 1));
|
|
46
|
+
if (record(value)) {
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(value, '$batchRef')) {
|
|
48
|
+
keys(value, ['$batchRef']);
|
|
49
|
+
const ref = value.$batchRef;
|
|
50
|
+
if (!record(ref))
|
|
51
|
+
fail();
|
|
52
|
+
keys(ref, ['operation', 'path']);
|
|
53
|
+
if (typeof ref.operation !== 'string' || typeof ref.path !== 'string')
|
|
54
|
+
fail();
|
|
55
|
+
return visit(ref);
|
|
56
|
+
}
|
|
57
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => {
|
|
58
|
+
if (forbidden.has(key))
|
|
59
|
+
fail();
|
|
60
|
+
return [key, walk(child, visit, depth + 1)];
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (value !== null && typeof value === 'object')
|
|
64
|
+
fail();
|
|
65
|
+
if (typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint' || value === undefined)
|
|
66
|
+
fail();
|
|
67
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
68
|
+
fail();
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function validateAtomicBatch(options) {
|
|
72
|
+
if (!record(options))
|
|
73
|
+
fail();
|
|
74
|
+
keys(options, ['product', 'env', 'database', 'session', 'system', 'transaction', 'operations']);
|
|
75
|
+
for (const value of [options.product, options.env, options.database])
|
|
76
|
+
if (typeof value !== 'string' || !value.trim())
|
|
77
|
+
fail();
|
|
78
|
+
if (options.session !== undefined && (typeof options.session !== 'string' || !options.session.trim()))
|
|
79
|
+
fail();
|
|
80
|
+
if (options.system !== undefined) {
|
|
81
|
+
if (!record(options.system))
|
|
82
|
+
fail();
|
|
83
|
+
keys(options.system, ['reason']);
|
|
84
|
+
if (typeof options.system.reason !== 'string' || !options.system.reason.trim())
|
|
85
|
+
fail();
|
|
86
|
+
}
|
|
87
|
+
if (!!options.session === !!options.system)
|
|
88
|
+
fail();
|
|
89
|
+
if (!Array.isArray(options.operations) || !options.operations.length || options.operations.length > 100)
|
|
90
|
+
fail();
|
|
91
|
+
const prior = new Set();
|
|
92
|
+
for (const op of options.operations) {
|
|
93
|
+
if (!record(op))
|
|
94
|
+
fail();
|
|
95
|
+
keys(op, ['name', 'operation', 'table', 'data', 'where', 'conflictKeys', 'returning', 'expectedCount', 'project']);
|
|
96
|
+
if (typeof op.name !== 'string' || !/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/.test(op.name) || prior.has(op.name) || forbidden.has(op.name))
|
|
97
|
+
fail();
|
|
98
|
+
if (typeof op.table !== 'string' || !op.table.trim() || op.table.startsWith('_ductape_') || op.table.startsWith('system.'))
|
|
99
|
+
fail();
|
|
100
|
+
if (!['insert', 'update', 'delete', 'upsert'].includes(op.operation))
|
|
101
|
+
fail();
|
|
102
|
+
if (op.operation !== 'delete' && !record(op.data) && !(op.operation === 'insert' && Array.isArray(op.data) && op.data.length && op.data.every(record)))
|
|
103
|
+
fail();
|
|
104
|
+
if (['update', 'delete'].includes(op.operation) && (!record(op.where) || !Object.keys(op.where).length))
|
|
105
|
+
fail();
|
|
106
|
+
if (op.operation === 'upsert' && (!Array.isArray(op.conflictKeys) || !op.conflictKeys.length || op.conflictKeys.some(key => typeof key !== 'string' || !key || forbidden.has(key))))
|
|
107
|
+
fail();
|
|
108
|
+
if (op.returning !== undefined && typeof op.returning !== 'boolean')
|
|
109
|
+
fail();
|
|
110
|
+
if (op.expectedCount !== undefined && (!Number.isSafeInteger(op.expectedCount) || op.expectedCount < 0))
|
|
111
|
+
fail();
|
|
112
|
+
if (op.project !== undefined && (!record(op.project) || Object.values(op.project).some(path => typeof path !== 'string' || (path !== '' && !path.startsWith('/')))))
|
|
113
|
+
fail();
|
|
114
|
+
walk(op, ref => { if (!prior.has(ref.operation))
|
|
115
|
+
fail(); return null; });
|
|
116
|
+
prior.add(op.name);
|
|
117
|
+
}
|
|
118
|
+
if (Buffer.byteLength(JSON.stringify(options.operations)) > 1024 * 1024)
|
|
119
|
+
fail();
|
|
120
|
+
}
|
|
121
|
+
/** Sequential ACID executor. No automatic write replay and no provider callbacks. */
|
|
122
|
+
async function executeAtomicBatch(host, options) {
|
|
123
|
+
var _a, _b, _c;
|
|
124
|
+
validateAtomicBatch(options);
|
|
125
|
+
// Snapshot caller-owned inputs before awaiting; preserve the opaque session and transaction.
|
|
126
|
+
options = Object.assign(Object.assign({}, options), { operations: JSON.parse(JSON.stringify(options.operations)) });
|
|
127
|
+
// References are resolved later, so use a JSON snapshot retaining their declarative form.
|
|
128
|
+
await host.preflight(options);
|
|
129
|
+
const native = await ((_a = host.executeNative) === null || _a === void 0 ? void 0 : _a.call(host, options));
|
|
130
|
+
if (native)
|
|
131
|
+
return native;
|
|
132
|
+
const transaction = (_b = options.transaction) !== null && _b !== void 0 ? _b : await host.begin(options);
|
|
133
|
+
const results = Object.create(null);
|
|
134
|
+
const projected = Object.create(null);
|
|
135
|
+
let operation;
|
|
136
|
+
let guard = false;
|
|
137
|
+
try {
|
|
138
|
+
await ((_c = host.validateTransaction) === null || _c === void 0 ? void 0 : _c.call(host, options, transaction));
|
|
139
|
+
for (const source of options.operations) {
|
|
140
|
+
operation = source.name;
|
|
141
|
+
if (!transaction.isActive())
|
|
142
|
+
throw new Error('inactive transaction');
|
|
143
|
+
const op = walk(source, ref => pointer(results[ref.operation], ref.path));
|
|
144
|
+
const result = await host.execute(op, options, transaction);
|
|
145
|
+
if (op.expectedCount !== undefined && result.count !== op.expectedCount) {
|
|
146
|
+
guard = true;
|
|
147
|
+
throw new Error('count guard failed');
|
|
148
|
+
}
|
|
149
|
+
results[op.name] = result;
|
|
150
|
+
projected[op.name] = op.project ? Object.fromEntries(Object.entries(op.project).map(([key, path]) => [key, pointer(result, path)])) : result;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (_d) {
|
|
154
|
+
let outcome = transaction.status === 'rolled_back' ? 'aborted' : 'rollback_unknown';
|
|
155
|
+
try {
|
|
156
|
+
if (transaction.isActive()) {
|
|
157
|
+
await transaction.rollback();
|
|
158
|
+
outcome = 'aborted';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch ( /* Preserve uncertainty, not the provider payload. */_e) { /* Preserve uncertainty, not the provider payload. */ }
|
|
162
|
+
throw new AtomicBatchError(guard ? 'BATCH_GUARD_FAILED' : 'BATCH_OPERATION_FAILED', outcome, operation, transaction.id);
|
|
163
|
+
}
|
|
164
|
+
if (options.transaction)
|
|
165
|
+
return { status: 'pending', transactionId: transaction.id, results: projected };
|
|
166
|
+
try {
|
|
167
|
+
await transaction.commit();
|
|
168
|
+
}
|
|
169
|
+
catch (_f) {
|
|
170
|
+
throw new AtomicBatchError('BATCH_COMMIT_UNKNOWN', 'commit_unknown', undefined, transaction.id);
|
|
171
|
+
}
|
|
172
|
+
return { status: 'committed', transactionId: transaction.id, results: projected };
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=atomic-batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-batch.js","sourceRoot":"","sources":["../../src/database/atomic-batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { IConnectionConfig, IConnectionResult, IConnectionContext, IDatabaseConfig } from './types/connection.interface';
|
|
11
11
|
import { IQueryOptions, IQueryResult, IRawQueryOptions, IRawQueryResult } from './types/query.interface';
|
|
12
12
|
import { IInsertOptions, IInsertResult, IUpdateOptions, IUpdateResult, IDeleteOptions, IDeleteResult, IUpsertOptions, IUpsertResult } from './types/write.interface';
|
|
13
|
+
import { AtomicBatchOptions, AtomicBatchResult } from './atomic-batch';
|
|
13
14
|
import { ITransactionOptions, ITransaction, ITransactionCallback } from './types/transaction.interface';
|
|
14
15
|
import { IAggregateOptions, IAggregateResult, ICountOptions, ISumOptions, IAvgOptions, IMinMaxOptions, IGroupByOptions, IGroupByResult } from './types/aggregation.interface';
|
|
15
16
|
import { ICreateTableOptions, ITableDefinition, IAlterTableOperation, ITableSchema, ITableInfo, ICreateIndexOptions, IDropIndexOptions, IListIndexesOptions, IIndexInfo, IIndexStatistics, SimpleFieldType, ISimpleFieldDefinition, ISimpleSchemaDefinition, ISimpleCreateOptions, ISimpleDropOptions, ISimpleIndexOptions } from './types/schema.interface';
|
|
@@ -67,6 +68,7 @@ export declare class DatabaseService {
|
|
|
67
68
|
private currentContext;
|
|
68
69
|
private adapterFactory;
|
|
69
70
|
private transactionManager;
|
|
71
|
+
private readonly transactionScopes;
|
|
70
72
|
private schemaManagers;
|
|
71
73
|
private migrationEngines;
|
|
72
74
|
private actionManager;
|
|
@@ -605,6 +607,8 @@ export declare class DatabaseService {
|
|
|
605
607
|
* }
|
|
606
608
|
*/
|
|
607
609
|
beginTransaction(options: ITransactionOptions): Promise<ITransaction>;
|
|
610
|
+
/** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
|
|
611
|
+
atomicBatch(options: AtomicBatchOptions): Promise<AtomicBatchResult>;
|
|
608
612
|
/**
|
|
609
613
|
* Create a new table
|
|
610
614
|
*
|
|
@@ -48,10 +48,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
48
48
|
exports.databaseService = exports.DatabaseConnection = exports.DatabaseService = void 0;
|
|
49
49
|
const enums_1 = require("./types/enums");
|
|
50
50
|
const presave_processor_1 = require("./presave/presave-processor");
|
|
51
|
+
const atomic_batch_1 = require("./atomic-batch");
|
|
52
|
+
const dynamodb_adapter_1 = require("./adapters/dynamodb.adapter");
|
|
53
|
+
const transaction_manager_1 = require("./transactions/transaction-manager");
|
|
54
|
+
const mongodb_adapter_1 = require("./adapters/mongodb.adapter");
|
|
55
|
+
const postgresql_adapter_1 = require("./adapters/postgresql.adapter");
|
|
56
|
+
const mysql_adapter_1 = require("./adapters/mysql.adapter");
|
|
57
|
+
const context_1 = require("../context");
|
|
51
58
|
const adapter_factory_1 = require("./adapters/adapter.factory");
|
|
52
59
|
const query_builder_1 = require("./operators/query-builder");
|
|
53
60
|
const aggregation_builder_1 = require("./operators/aggregation-builder");
|
|
54
|
-
const
|
|
61
|
+
const transaction_manager_2 = require("./transactions/transaction-manager");
|
|
55
62
|
const schema_manager_1 = require("./schema/schema-manager");
|
|
56
63
|
const migration_engine_1 = require("./migrations/migration-engine");
|
|
57
64
|
const action_manager_1 = require("./actions/action-manager");
|
|
@@ -94,6 +101,7 @@ class DatabaseService {
|
|
|
94
101
|
this.adapters = new Map();
|
|
95
102
|
this.connectionContexts = new Map();
|
|
96
103
|
this.currentContext = null;
|
|
104
|
+
this.transactionScopes = new WeakMap();
|
|
97
105
|
this.schemaManagers = new Map();
|
|
98
106
|
this.migrationEngines = new Map();
|
|
99
107
|
/** Service configuration */
|
|
@@ -116,7 +124,7 @@ class DatabaseService {
|
|
|
116
124
|
this.config = config || null;
|
|
117
125
|
this.preInitializedProductBuilder = config === null || config === void 0 ? void 0 : config.preInitializedProductBuilder;
|
|
118
126
|
this.adapterFactory = new adapter_factory_1.AdapterFactory();
|
|
119
|
-
this.transactionManager = new
|
|
127
|
+
this.transactionManager = new transaction_manager_2.TransactionManager();
|
|
120
128
|
this.actionManager = new action_manager_1.ActionManager(this);
|
|
121
129
|
this._privateKey = (config === null || config === void 0 ? void 0 : config.private_key) || '';
|
|
122
130
|
if ((config === null || config === void 0 ? void 0 : config.default_product) || (config === null || config === void 0 ? void 0 : config.default_env)) {
|
|
@@ -1973,7 +1981,109 @@ class DatabaseService {
|
|
|
1973
1981
|
async beginTransaction(options) {
|
|
1974
1982
|
await this.ensureAdapterFor(options);
|
|
1975
1983
|
const adapter = this.getAdapter(options);
|
|
1976
|
-
|
|
1984
|
+
const transaction = await this.transactionManager.beginTransaction(adapter, options);
|
|
1985
|
+
this.transactionScopes.set(transaction, JSON.stringify([options.product, options.env, options.database]));
|
|
1986
|
+
return transaction;
|
|
1987
|
+
}
|
|
1988
|
+
/** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
|
|
1989
|
+
async atomicBatch(options) {
|
|
1990
|
+
var _a;
|
|
1991
|
+
const logs = [];
|
|
1992
|
+
const batch = { id: (0, processor_utils_1.generateObjectId)(), logs, system: !!options.system };
|
|
1993
|
+
let outcome = 'not_started';
|
|
1994
|
+
try {
|
|
1995
|
+
const result = await (0, context_1.runWithExecutionContext)((0, context_1.createExecutionContext)({ database_batch: batch }), () => (0, atomic_batch_1.executeAtomicBatch)({
|
|
1996
|
+
preflight: async (scope) => {
|
|
1997
|
+
var _a;
|
|
1998
|
+
await this.ensureAdapterFor(scope);
|
|
1999
|
+
if (((_a = this.currentContext) === null || _a === void 0 ? void 0 : _a.product) && this.currentContext.product !== scope.product) {
|
|
2000
|
+
throw new Error('Atomic batch product must match the active database context');
|
|
2001
|
+
}
|
|
2002
|
+
if (scope.session && !(await this.getProductBuilder(scope.product)).fetchPrivateKey()) {
|
|
2003
|
+
throw new Error('Atomic batch cannot verify the provided user session');
|
|
2004
|
+
}
|
|
2005
|
+
const adapter = this.getAdapter(scope);
|
|
2006
|
+
if (!(adapter instanceof mongodb_adapter_1.MongoDBAdapter || adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter || adapter instanceof dynamodb_adapter_1.DynamoDBAdapter)) {
|
|
2007
|
+
throw new Error('This adapter cannot provide the required multi-table ACID contract; Cassandra logged batches are not a substitute');
|
|
2008
|
+
}
|
|
2009
|
+
if (scope.transaction && (!(scope.transaction instanceof transaction_manager_1.Transaction) || !scope.transaction.belongsTo(adapter) || !scope.transaction.isActive()
|
|
2010
|
+
|| this.transactionScopes.get(scope.transaction) !== JSON.stringify([scope.product, scope.env, scope.database]))) {
|
|
2011
|
+
throw new Error('Atomic batch transaction does not belong to the active database connection');
|
|
2012
|
+
}
|
|
2013
|
+
if (scope.transaction && (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter)
|
|
2014
|
+
&& scope.transaction.isolationLevel !== enums_1.IsolationLevel.SERIALIZABLE) {
|
|
2015
|
+
throw new Error('Borrowed SQL batch transactions must use SERIALIZABLE isolation');
|
|
2016
|
+
}
|
|
2017
|
+
const triggers = await this.fetchTriggers(scope.product, scope.database);
|
|
2018
|
+
if (triggers.length)
|
|
2019
|
+
throw new Error('Atomic batch does not yet support configured triggers; use a transaction-local outbox');
|
|
2020
|
+
if (adapter instanceof mongodb_adapter_1.MongoDBAdapter)
|
|
2021
|
+
await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))]);
|
|
2022
|
+
},
|
|
2023
|
+
executeNative: async (scope) => {
|
|
2024
|
+
var _a, _b, _c, _d;
|
|
2025
|
+
const adapter = this.getAdapter(scope);
|
|
2026
|
+
if (!(adapter instanceof dynamodb_adapter_1.DynamoDBAdapter))
|
|
2027
|
+
return undefined;
|
|
2028
|
+
let resolved = scope;
|
|
2029
|
+
let sessionLogFields = {};
|
|
2030
|
+
if (scope.session) {
|
|
2031
|
+
const builder = await this.getProductBuilder(scope.product);
|
|
2032
|
+
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../sessions')));
|
|
2033
|
+
const result = await processSessionForExecution(scope.session, builder.fetchPrivateKey(), scope, scope.env, {
|
|
2034
|
+
fetchSessionSelector: async (tag) => { var _a; return (_a = (await builder.fetchSession(tag))) === null || _a === void 0 ? void 0 : _a.selector; },
|
|
2035
|
+
});
|
|
2036
|
+
if (result.error)
|
|
2037
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_VALIDATION', 'not_started');
|
|
2038
|
+
resolved = Object.assign(Object.assign({}, scope), { operations: result.input.operations });
|
|
2039
|
+
sessionLogFields = result.logFields;
|
|
2040
|
+
}
|
|
2041
|
+
this.initializeLogService();
|
|
2042
|
+
const start = Date.now();
|
|
2043
|
+
try {
|
|
2044
|
+
const result = await adapter.atomicBatch(resolved);
|
|
2045
|
+
(_a = this.logService) === null || _a === void 0 ? void 0 : _a.add(Object.assign({ product_tag: scope.product, workspace_id: ((_b = this.config) === null || _b === void 0 ? void 0 : _b.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: result.transactionId, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - success', status: logs_types_1.LogEventStatus.SUCCESS, successful_execution: true, data: { operations: scope.operations.map(op => ({ name: op.name, operation: op.operation, table: op.table })) } }, sessionLogFields));
|
|
2046
|
+
return result;
|
|
2047
|
+
}
|
|
2048
|
+
catch (error) {
|
|
2049
|
+
(_c = this.logService) === null || _c === void 0 ? void 0 : _c.add(Object.assign({ product_tag: scope.product, workspace_id: ((_d = this.config) === null || _d === void 0 ? void 0 : _d.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: batch.id, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - failed', status: logs_types_1.LogEventStatus.FAIL, failed_execution: true, data: { outcome: error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started' } }, sessionLogFields));
|
|
2050
|
+
throw error;
|
|
2051
|
+
}
|
|
2052
|
+
},
|
|
2053
|
+
validateTransaction: async (scope, transaction) => {
|
|
2054
|
+
const adapter = this.getAdapter(scope);
|
|
2055
|
+
if (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter) {
|
|
2056
|
+
await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))], transaction);
|
|
2057
|
+
}
|
|
2058
|
+
},
|
|
2059
|
+
begin: (scope) => this.beginTransaction({ product: scope.product, env: scope.env, database: scope.database, isolationLevel: enums_1.IsolationLevel.SERIALIZABLE }),
|
|
2060
|
+
execute: async (op, scope, transaction) => {
|
|
2061
|
+
const input = { product: scope.product, env: scope.env, database: scope.database, session: scope.session,
|
|
2062
|
+
table: op.table, transaction, returning: op.returning };
|
|
2063
|
+
switch (op.operation) {
|
|
2064
|
+
case 'insert': return this.insert(Object.assign(Object.assign({}, input), { data: op.data }));
|
|
2065
|
+
case 'update': return this.update(Object.assign(Object.assign({}, input), { where: op.where, data: op.data }));
|
|
2066
|
+
case 'delete': return this.delete(Object.assign(Object.assign({}, input), { where: op.where }));
|
|
2067
|
+
case 'upsert': return this.upsert(Object.assign(Object.assign({}, input), { data: op.data, conflictKeys: op.conflictKeys }));
|
|
2068
|
+
}
|
|
2069
|
+
},
|
|
2070
|
+
}, options));
|
|
2071
|
+
outcome = result.status;
|
|
2072
|
+
return result;
|
|
2073
|
+
}
|
|
2074
|
+
catch (error) {
|
|
2075
|
+
outcome = error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started';
|
|
2076
|
+
throw error;
|
|
2077
|
+
}
|
|
2078
|
+
finally {
|
|
2079
|
+
for (const emit of logs) {
|
|
2080
|
+
try {
|
|
2081
|
+
emit(outcome);
|
|
2082
|
+
}
|
|
2083
|
+
catch ( /* Telemetry cannot change transaction outcome. */_b) { /* Telemetry cannot change transaction outcome. */ }
|
|
2084
|
+
}
|
|
2085
|
+
void ((_a = this.logService) === null || _a === void 0 ? void 0 : _a.publish().catch(() => { }));
|
|
2086
|
+
}
|
|
1977
2087
|
}
|
|
1978
2088
|
// ==================== SCHEMA OPERATIONS ====================
|
|
1979
2089
|
/**
|
|
@@ -2839,6 +2949,9 @@ class DatabaseService {
|
|
|
2839
2949
|
return await fn(adapter);
|
|
2840
2950
|
}
|
|
2841
2951
|
catch (error) {
|
|
2952
|
+
// An uncertain transactional write must never be replayed on a fresh connection.
|
|
2953
|
+
if (options.transaction)
|
|
2954
|
+
throw error;
|
|
2842
2955
|
if (!this.isConnectionError(error))
|
|
2843
2956
|
throw error;
|
|
2844
2957
|
await this.connect({
|