@dbos-inc/node-pg-datasource 3.0.11-preview.gc9233b8190 → 3.0.16-preview
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/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -54
- package/dist/index.js.map +1 -1
- package/index.ts +129 -81
- package/package.json +6 -5
- package/tests/config.test.ts +2 -2
- package/tests/datasource.test.ts +184 -63
- package/tests/test-helpers.ts +3 -2
- package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { PGTransactionConfig
|
|
1
|
+
import { PGTransactionConfig, DBOSDataSource } from '@dbos-inc/dbos-sdk/datasource';
|
|
2
2
|
import { type ClientBase, type ClientConfig, type PoolConfig } from 'pg';
|
|
3
|
+
interface NodePostgresTransactionOptions extends PGTransactionConfig {
|
|
4
|
+
name?: string;
|
|
5
|
+
}
|
|
3
6
|
export { NodePostgresTransactionOptions };
|
|
4
7
|
export declare class NodePostgresDataSource implements DBOSDataSource<NodePostgresTransactionOptions> {
|
|
5
8
|
#private;
|
|
6
|
-
static get client(): ClientBase;
|
|
7
|
-
static initializeInternalSchema(config: ClientConfig): Promise<void>;
|
|
8
9
|
readonly name: string;
|
|
10
|
+
static get client(): ClientBase;
|
|
11
|
+
get client(): ClientBase;
|
|
12
|
+
static initializeDBOSSchema(config: ClientConfig): Promise<void>;
|
|
9
13
|
constructor(name: string, config: PoolConfig);
|
|
10
|
-
runTransaction<T>(
|
|
11
|
-
registerTransaction<This, Args extends unknown[], Return>(func: (this: This, ...args: Args) => Promise<Return>,
|
|
12
|
-
transaction(config?: NodePostgresTransactionOptions): <This, Args extends unknown[], Return>(_target: object, propertyKey:
|
|
14
|
+
runTransaction<T>(func: () => Promise<T>, config?: NodePostgresTransactionOptions): Promise<T>;
|
|
15
|
+
registerTransaction<This, Args extends unknown[], Return>(func: (this: This, ...args: Args) => Promise<Return>, config?: NodePostgresTransactionOptions): (this: This, ...args: Args) => Promise<Return>;
|
|
16
|
+
transaction(config?: NodePostgresTransactionOptions): <This, Args extends unknown[], Return>(_target: object, propertyKey: PropertyKey, descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>) => TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>;
|
|
13
17
|
}
|
|
14
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EAQL,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EAQL,mBAAmB,EACnB,cAAc,EAEf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,YAAY,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AASvF,UAAU,8BAA+B,SAAQ,mBAAmB;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,OAAO,EAAE,8BAA8B,EAAE,CAAC;AAwL1C,qBAAa,sBAAuB,YAAW,cAAc,CAAC,8BAA8B,CAAC;;IAqCzF,QAAQ,CAAC,IAAI,EAAE,MAAM;IAtBvB,MAAM,KAAK,MAAM,eAEhB;IAED,IAAI,MAAM,eAET;WAEY,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;gBAc3D,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,UAAU;IAMd,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,8BAA8B;IAIvF,mBAAmB,CAAC,IAAI,EAAE,IAAI,SAAS,OAAO,EAAE,EAAE,MAAM,EACtD,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,EACpD,MAAM,CAAC,EAAE,8BAA8B,GACtC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC;IAIjD,WAAW,CAAC,MAAM,CAAC,EAAE,8BAA8B,mDAItC,MAAM,eACF,WAAW,cACZ,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,QAAQ,MAAM,CAAC,CAAC,oCAAxC,IAAI,WAAW,IAAI,KAAK,QAAQ,MAAM,CAAC;CAcvF"}
|
package/dist/index.js
CHANGED
|
@@ -8,32 +8,64 @@ const pg_1 = require("pg");
|
|
|
8
8
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
9
9
|
const superjson_1 = require("superjson");
|
|
10
10
|
const asyncLocalCtx = new node_async_hooks_1.AsyncLocalStorage();
|
|
11
|
-
class
|
|
11
|
+
class NodePostgresTransactionHandler {
|
|
12
12
|
name;
|
|
13
|
+
config;
|
|
13
14
|
dsType = 'NodePostgresDataSource';
|
|
14
|
-
#
|
|
15
|
+
#poolField;
|
|
15
16
|
constructor(name, config) {
|
|
16
17
|
this.name = name;
|
|
17
|
-
this
|
|
18
|
+
this.config = config;
|
|
18
19
|
}
|
|
19
|
-
initialize() {
|
|
20
|
-
|
|
20
|
+
async initialize() {
|
|
21
|
+
const pool = this.#poolField;
|
|
22
|
+
this.#poolField = new pg_1.Pool(this.config);
|
|
23
|
+
await pool?.end();
|
|
21
24
|
}
|
|
22
|
-
destroy() {
|
|
23
|
-
|
|
25
|
+
async destroy() {
|
|
26
|
+
const pool = this.#poolField;
|
|
27
|
+
this.#poolField = undefined;
|
|
28
|
+
await pool?.end();
|
|
24
29
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
+
get #pool() {
|
|
31
|
+
if (!this.#poolField) {
|
|
32
|
+
throw new Error(`DataSource ${this.name} is not initialized.`);
|
|
33
|
+
}
|
|
34
|
+
return this.#poolField;
|
|
35
|
+
}
|
|
36
|
+
async #checkExecution(workflowID, stepID) {
|
|
37
|
+
const { rows } = await this.#pool.query(
|
|
38
|
+
/*sql*/
|
|
39
|
+
`SELECT output, error FROM dbos.transaction_completion
|
|
40
|
+
WHERE workflow_id = $1 AND function_num = $2`, [workflowID, stepID]);
|
|
41
|
+
if (rows.length === 0) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const { output, error } = rows[0];
|
|
45
|
+
return error !== null ? { error } : { output };
|
|
30
46
|
}
|
|
31
|
-
static async #recordOutput(client, workflowID,
|
|
47
|
+
static async #recordOutput(client, workflowID, stepID, output) {
|
|
32
48
|
try {
|
|
33
49
|
await client.query(
|
|
34
50
|
/*sql*/
|
|
35
51
|
`INSERT INTO dbos.transaction_completion (workflow_id, function_num, output)
|
|
36
|
-
VALUES ($1, $2, $3)`, [workflowID,
|
|
52
|
+
VALUES ($1, $2, $3)`, [workflowID, stepID, output]);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if ((0, datasource_1.isPGKeyConflictError)(error)) {
|
|
56
|
+
throw new dbos_sdk_1.DBOSWorkflowConflictError(workflowID);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async #recordError(workflowID, stepID, error) {
|
|
64
|
+
try {
|
|
65
|
+
await this.#pool.query(
|
|
66
|
+
/*sql*/
|
|
67
|
+
`INSERT INTO dbos.transaction_completion (workflow_id, function_num, error)
|
|
68
|
+
VALUES ($1, $2, $3)`, [workflowID, stepID, error]);
|
|
37
69
|
}
|
|
38
70
|
catch (error) {
|
|
39
71
|
if ((0, datasource_1.isPGKeyConflictError)(error)) {
|
|
@@ -65,57 +97,52 @@ class NodePGDSTH {
|
|
|
65
97
|
}
|
|
66
98
|
async invokeTransactionFunction(config, target, func, ...args) {
|
|
67
99
|
const workflowID = dbos_sdk_1.DBOS.workflowID;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const functionNum = dbos_sdk_1.DBOS.stepID;
|
|
72
|
-
if (functionNum === undefined) {
|
|
73
|
-
throw new Error('Function Number is not set.');
|
|
100
|
+
const stepID = dbos_sdk_1.DBOS.stepID;
|
|
101
|
+
if (workflowID !== undefined && stepID === undefined) {
|
|
102
|
+
throw new Error('DBOS.stepID is undefined inside a workflow.');
|
|
74
103
|
}
|
|
75
104
|
const readOnly = config?.readOnly ?? false;
|
|
105
|
+
const saveResults = !readOnly && workflowID !== undefined;
|
|
106
|
+
// Retry loop if appropriate
|
|
76
107
|
let retryWaitMS = 1;
|
|
77
108
|
const backoffFactor = 1.5;
|
|
78
|
-
const maxRetryWaitMS = 2000;
|
|
109
|
+
const maxRetryWaitMS = 2000; // Maximum wait 2 seconds.
|
|
79
110
|
while (true) {
|
|
111
|
+
// Check to see if this tx has already been executed
|
|
112
|
+
const previousResult = saveResults ? await this.#checkExecution(workflowID, stepID) : undefined;
|
|
113
|
+
if (previousResult) {
|
|
114
|
+
dbos_sdk_1.DBOS.span?.setAttribute('cached', true);
|
|
115
|
+
if ('error' in previousResult) {
|
|
116
|
+
throw superjson_1.SuperJSON.parse(previousResult.error);
|
|
117
|
+
}
|
|
118
|
+
return (previousResult.output ? superjson_1.SuperJSON.parse(previousResult.output) : null);
|
|
119
|
+
}
|
|
80
120
|
try {
|
|
81
121
|
const result = await this.#transaction(async (client) => {
|
|
82
|
-
// Check to see if this tx has already been executed
|
|
83
|
-
const previousResult = readOnly || !workflowID ? undefined : await NodePGDSTH.#checkExecution(client, workflowID, functionNum);
|
|
84
|
-
if (previousResult) {
|
|
85
|
-
return (previousResult.output ? superjson_1.SuperJSON.parse(previousResult.output) : null);
|
|
86
|
-
}
|
|
87
122
|
// execute user's transaction function
|
|
88
|
-
const result = await asyncLocalCtx.run({ client }, async () => {
|
|
123
|
+
const result = await asyncLocalCtx.run({ client, owner: this }, async () => {
|
|
89
124
|
return (await func.call(target, ...args));
|
|
90
125
|
});
|
|
91
126
|
// save the output of read/write transactions
|
|
92
|
-
if (
|
|
93
|
-
await
|
|
94
|
-
// Note, existing code wraps #recordOutput call in a try/catch block that
|
|
95
|
-
// converts DB error with code 25P02 to DBOSFailedSqlTransactionError.
|
|
96
|
-
// However, existing code doesn't make any logic decisions based on that error type.
|
|
97
|
-
// DBOSFailedSqlTransactionError does stored WF ID and function name, so I assume that info is logged out somewhere
|
|
127
|
+
if (saveResults) {
|
|
128
|
+
await NodePostgresTransactionHandler.#recordOutput(client, workflowID, stepID, superjson_1.SuperJSON.stringify(result));
|
|
98
129
|
}
|
|
99
130
|
return result;
|
|
100
|
-
},
|
|
101
|
-
// TODO: span.setStatus({ code: SpanStatusCode.OK });
|
|
102
|
-
// TODO: this.tracer.endSpan(span);
|
|
131
|
+
}, config);
|
|
103
132
|
return result;
|
|
104
133
|
}
|
|
105
134
|
catch (error) {
|
|
106
135
|
if ((0, datasource_1.isPGRetriableTransactionError)(error)) {
|
|
107
|
-
|
|
136
|
+
dbos_sdk_1.DBOS.span?.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMS }, performance.now());
|
|
108
137
|
await new Promise((resolve) => setTimeout(resolve, retryWaitMS));
|
|
109
138
|
retryWaitMS = Math.min(retryWaitMS * backoffFactor, maxRetryWaitMS);
|
|
110
139
|
continue;
|
|
111
140
|
}
|
|
112
141
|
else {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// However, I'm concerned that we have a dueling execution hole where one tx fails while another succeeds.
|
|
118
|
-
// This implies that we can end up in a situation where the step output records an error but the txOutput table records success.
|
|
142
|
+
if (saveResults) {
|
|
143
|
+
const message = superjson_1.SuperJSON.stringify(error);
|
|
144
|
+
await this.#recordError(workflowID, stepID, message);
|
|
145
|
+
}
|
|
119
146
|
throw error;
|
|
120
147
|
}
|
|
121
148
|
}
|
|
@@ -123,17 +150,27 @@ class NodePGDSTH {
|
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
class NodePostgresDataSource {
|
|
126
|
-
|
|
153
|
+
name;
|
|
154
|
+
static #getClient(p) {
|
|
127
155
|
if (!dbos_sdk_1.DBOS.isInTransaction()) {
|
|
128
|
-
throw new Error('
|
|
156
|
+
throw new Error('Invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
|
|
129
157
|
}
|
|
130
158
|
const ctx = asyncLocalCtx.getStore();
|
|
131
159
|
if (!ctx) {
|
|
132
|
-
throw new Error('
|
|
160
|
+
throw new Error('Invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
|
|
161
|
+
}
|
|
162
|
+
if (p && p !== ctx.owner) {
|
|
163
|
+
throw new Error('Use of `NodePostgresDataSource.client` from the wrong object');
|
|
133
164
|
}
|
|
134
165
|
return ctx.client;
|
|
135
166
|
}
|
|
136
|
-
static
|
|
167
|
+
static get client() {
|
|
168
|
+
return NodePostgresDataSource.#getClient(undefined);
|
|
169
|
+
}
|
|
170
|
+
get client() {
|
|
171
|
+
return NodePostgresDataSource.#getClient(this.#provider);
|
|
172
|
+
}
|
|
173
|
+
static async initializeDBOSSchema(config) {
|
|
137
174
|
const client = new pg_1.Client(config);
|
|
138
175
|
try {
|
|
139
176
|
await client.connect();
|
|
@@ -144,18 +181,17 @@ class NodePostgresDataSource {
|
|
|
144
181
|
await client.end();
|
|
145
182
|
}
|
|
146
183
|
}
|
|
147
|
-
name;
|
|
148
184
|
#provider;
|
|
149
185
|
constructor(name, config) {
|
|
150
186
|
this.name = name;
|
|
151
|
-
this.#provider = new
|
|
187
|
+
this.#provider = new NodePostgresTransactionHandler(name, config);
|
|
152
188
|
(0, datasource_1.registerDataSource)(this.#provider);
|
|
153
189
|
}
|
|
154
|
-
async runTransaction(
|
|
155
|
-
return await (0, datasource_1.runTransaction)(
|
|
190
|
+
async runTransaction(func, config) {
|
|
191
|
+
return await (0, datasource_1.runTransaction)(func, config?.name ?? func.name, { dsName: this.name, config });
|
|
156
192
|
}
|
|
157
|
-
registerTransaction(func,
|
|
158
|
-
return (0, datasource_1.registerTransaction)(this.name, func, { name }, config);
|
|
193
|
+
registerTransaction(func, config) {
|
|
194
|
+
return (0, datasource_1.registerTransaction)(this.name, func, { name: config?.name ?? func.name }, config);
|
|
159
195
|
}
|
|
160
196
|
transaction(config) {
|
|
161
197
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -164,7 +200,10 @@ class NodePostgresDataSource {
|
|
|
164
200
|
if (!descriptor.value) {
|
|
165
201
|
throw Error('Use of decorator when original method is undefined');
|
|
166
202
|
}
|
|
167
|
-
descriptor.value = ds.registerTransaction(descriptor.value,
|
|
203
|
+
descriptor.value = ds.registerTransaction(descriptor.value, {
|
|
204
|
+
...config,
|
|
205
|
+
name: config?.name ?? String(propertyKey),
|
|
206
|
+
});
|
|
168
207
|
return descriptor;
|
|
169
208
|
};
|
|
170
209
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAAA,gDAAgD;;;AAEhD,iDAAqE;AACrE,8DAWuC;AACvC,2BAAuF;AACvF,uDAAqD;AACrD,yCAAsC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isPGKeyConflictError,
|
|
10
10
|
registerTransaction,
|
|
11
11
|
runTransaction,
|
|
12
|
-
PGTransactionConfig
|
|
12
|
+
PGTransactionConfig,
|
|
13
13
|
DBOSDataSource,
|
|
14
14
|
registerDataSource,
|
|
15
15
|
} from '@dbos-inc/dbos-sdk/datasource';
|
|
@@ -19,47 +19,67 @@ import { SuperJSON } from 'superjson';
|
|
|
19
19
|
|
|
20
20
|
interface NodePostgresDataSourceContext {
|
|
21
21
|
client: ClientBase;
|
|
22
|
+
owner: NodePostgresTransactionHandler;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface NodePostgresTransactionOptions extends PGTransactionConfig {
|
|
26
|
+
name?: string;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export { NodePostgresTransactionOptions };
|
|
25
30
|
|
|
26
31
|
const asyncLocalCtx = new AsyncLocalStorage<NodePostgresDataSourceContext>();
|
|
27
32
|
|
|
28
|
-
class
|
|
29
|
-
readonly name: string;
|
|
33
|
+
class NodePostgresTransactionHandler implements DataSourceTransactionHandler {
|
|
30
34
|
readonly dsType = 'NodePostgresDataSource';
|
|
31
|
-
|
|
35
|
+
#poolField: Pool | undefined;
|
|
36
|
+
|
|
37
|
+
constructor(
|
|
38
|
+
readonly name: string,
|
|
39
|
+
private readonly config: PoolConfig,
|
|
40
|
+
) {}
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.#
|
|
42
|
+
async initialize(): Promise<void> {
|
|
43
|
+
const pool = this.#poolField;
|
|
44
|
+
this.#poolField = new Pool(this.config);
|
|
45
|
+
await pool?.end();
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
async destroy(): Promise<void> {
|
|
49
|
+
const pool = this.#poolField;
|
|
50
|
+
this.#poolField = undefined;
|
|
51
|
+
await pool?.end();
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
get #pool(): Pool {
|
|
55
|
+
if (!this.#poolField) {
|
|
56
|
+
throw new Error(`DataSource ${this.name} is not initialized.`);
|
|
57
|
+
}
|
|
58
|
+
return this.#poolField;
|
|
44
59
|
}
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
client: ClientBase,
|
|
61
|
+
async #checkExecution(
|
|
48
62
|
workflowID: string,
|
|
49
|
-
|
|
50
|
-
): Promise<{ output: string | null } | undefined> {
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
stepID: number,
|
|
64
|
+
): Promise<{ output: string | null } | { error: string } | undefined> {
|
|
65
|
+
type Result = { output: string | null; error: string | null };
|
|
66
|
+
const { rows } = await this.#pool.query<Result>(
|
|
67
|
+
/*sql*/
|
|
68
|
+
`SELECT output, error FROM dbos.transaction_completion
|
|
53
69
|
WHERE workflow_id = $1 AND function_num = $2`,
|
|
54
|
-
[workflowID,
|
|
70
|
+
[workflowID, stepID],
|
|
55
71
|
);
|
|
56
|
-
|
|
72
|
+
if (rows.length === 0) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const { output, error } = rows[0];
|
|
76
|
+
return error !== null ? { error } : { output };
|
|
57
77
|
}
|
|
58
78
|
|
|
59
79
|
static async #recordOutput(
|
|
60
80
|
client: ClientBase,
|
|
61
81
|
workflowID: string,
|
|
62
|
-
|
|
82
|
+
stepID: number,
|
|
63
83
|
output: string | null,
|
|
64
84
|
): Promise<void> {
|
|
65
85
|
try {
|
|
@@ -67,7 +87,24 @@ class NodePGDSTH implements DataSourceTransactionHandler {
|
|
|
67
87
|
/*sql*/
|
|
68
88
|
`INSERT INTO dbos.transaction_completion (workflow_id, function_num, output)
|
|
69
89
|
VALUES ($1, $2, $3)`,
|
|
70
|
-
[workflowID,
|
|
90
|
+
[workflowID, stepID, output],
|
|
91
|
+
);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (isPGKeyConflictError(error)) {
|
|
94
|
+
throw new DBOSWorkflowConflictError(workflowID);
|
|
95
|
+
} else {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async #recordError(workflowID: string, stepID: number, error: string): Promise<void> {
|
|
102
|
+
try {
|
|
103
|
+
await this.#pool.query(
|
|
104
|
+
/*sql*/
|
|
105
|
+
`INSERT INTO dbos.transaction_completion (workflow_id, function_num, error)
|
|
106
|
+
VALUES ($1, $2, $3)`,
|
|
107
|
+
[workflowID, stepID, error],
|
|
71
108
|
);
|
|
72
109
|
} catch (error) {
|
|
73
110
|
if (isPGKeyConflictError(error)) {
|
|
@@ -107,67 +144,64 @@ class NodePGDSTH implements DataSourceTransactionHandler {
|
|
|
107
144
|
...args: Args
|
|
108
145
|
): Promise<Return> {
|
|
109
146
|
const workflowID = DBOS.workflowID;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const functionNum = DBOS.stepID;
|
|
114
|
-
if (functionNum === undefined) {
|
|
115
|
-
throw new Error('Function Number is not set.');
|
|
147
|
+
const stepID = DBOS.stepID;
|
|
148
|
+
if (workflowID !== undefined && stepID === undefined) {
|
|
149
|
+
throw new Error('DBOS.stepID is undefined inside a workflow.');
|
|
116
150
|
}
|
|
117
151
|
|
|
118
152
|
const readOnly = config?.readOnly ?? false;
|
|
153
|
+
const saveResults = !readOnly && workflowID !== undefined;
|
|
154
|
+
|
|
155
|
+
// Retry loop if appropriate
|
|
119
156
|
let retryWaitMS = 1;
|
|
120
157
|
const backoffFactor = 1.5;
|
|
121
|
-
const maxRetryWaitMS = 2000;
|
|
158
|
+
const maxRetryWaitMS = 2000; // Maximum wait 2 seconds.
|
|
122
159
|
|
|
123
160
|
while (true) {
|
|
161
|
+
// Check to see if this tx has already been executed
|
|
162
|
+
const previousResult = saveResults ? await this.#checkExecution(workflowID, stepID!) : undefined;
|
|
163
|
+
if (previousResult) {
|
|
164
|
+
DBOS.span?.setAttribute('cached', true);
|
|
165
|
+
|
|
166
|
+
if ('error' in previousResult) {
|
|
167
|
+
throw SuperJSON.parse(previousResult.error);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return (previousResult.output ? SuperJSON.parse(previousResult.output) : null) as Return;
|
|
171
|
+
}
|
|
172
|
+
|
|
124
173
|
try {
|
|
125
|
-
const result = await this.#transaction<Return>(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Note, existing code wraps #recordOutput call in a try/catch block that
|
|
144
|
-
// converts DB error with code 25P02 to DBOSFailedSqlTransactionError.
|
|
145
|
-
// However, existing code doesn't make any logic decisions based on that error type.
|
|
146
|
-
// DBOSFailedSqlTransactionError does stored WF ID and function name, so I assume that info is logged out somewhere
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return result;
|
|
150
|
-
},
|
|
151
|
-
{ isolationLevel: config?.isolationLevel, readOnly: config?.readOnly },
|
|
152
|
-
);
|
|
153
|
-
// TODO: span.setStatus({ code: SpanStatusCode.OK });
|
|
154
|
-
// TODO: this.tracer.endSpan(span);
|
|
174
|
+
const result = await this.#transaction<Return>(async (client) => {
|
|
175
|
+
// execute user's transaction function
|
|
176
|
+
const result = await asyncLocalCtx.run({ client, owner: this }, async () => {
|
|
177
|
+
return (await func.call(target, ...args)) as Return;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// save the output of read/write transactions
|
|
181
|
+
if (saveResults) {
|
|
182
|
+
await NodePostgresTransactionHandler.#recordOutput(
|
|
183
|
+
client,
|
|
184
|
+
workflowID,
|
|
185
|
+
stepID!,
|
|
186
|
+
SuperJSON.stringify(result),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return result;
|
|
191
|
+
}, config);
|
|
155
192
|
|
|
156
193
|
return result;
|
|
157
194
|
} catch (error) {
|
|
158
195
|
if (isPGRetriableTransactionError(error)) {
|
|
159
|
-
|
|
196
|
+
DBOS.span?.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMS }, performance.now());
|
|
160
197
|
await new Promise((resolve) => setTimeout(resolve, retryWaitMS));
|
|
161
198
|
retryWaitMS = Math.min(retryWaitMS * backoffFactor, maxRetryWaitMS);
|
|
162
199
|
continue;
|
|
163
200
|
} else {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// For normal execution, this is fine because we also store tx step results (output and error) in the sysdb operation output table.
|
|
169
|
-
// However, I'm concerned that we have a dueling execution hole where one tx fails while another succeeds.
|
|
170
|
-
// This implies that we can end up in a situation where the step output records an error but the txOutput table records success.
|
|
201
|
+
if (saveResults) {
|
|
202
|
+
const message = SuperJSON.stringify(error);
|
|
203
|
+
await this.#recordError(workflowID, stepID!, message);
|
|
204
|
+
}
|
|
171
205
|
|
|
172
206
|
throw error;
|
|
173
207
|
}
|
|
@@ -177,18 +211,29 @@ class NodePGDSTH implements DataSourceTransactionHandler {
|
|
|
177
211
|
}
|
|
178
212
|
|
|
179
213
|
export class NodePostgresDataSource implements DBOSDataSource<NodePostgresTransactionOptions> {
|
|
180
|
-
static
|
|
214
|
+
static #getClient(p?: NodePostgresTransactionHandler): ClientBase {
|
|
181
215
|
if (!DBOS.isInTransaction()) {
|
|
182
|
-
throw new Error('
|
|
216
|
+
throw new Error('Invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
|
|
183
217
|
}
|
|
184
218
|
const ctx = asyncLocalCtx.getStore();
|
|
185
219
|
if (!ctx) {
|
|
186
|
-
throw new Error('
|
|
220
|
+
throw new Error('Invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
|
|
221
|
+
}
|
|
222
|
+
if (p && p !== ctx.owner) {
|
|
223
|
+
throw new Error('Use of `NodePostgresDataSource.client` from the wrong object');
|
|
187
224
|
}
|
|
188
225
|
return ctx.client;
|
|
189
226
|
}
|
|
190
227
|
|
|
191
|
-
static
|
|
228
|
+
static get client() {
|
|
229
|
+
return NodePostgresDataSource.#getClient(undefined);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
get client() {
|
|
233
|
+
return NodePostgresDataSource.#getClient(this.#provider);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static async initializeDBOSSchema(config: ClientConfig): Promise<void> {
|
|
192
237
|
const client = new Client(config);
|
|
193
238
|
try {
|
|
194
239
|
await client.connect();
|
|
@@ -199,25 +244,25 @@ export class NodePostgresDataSource implements DBOSDataSource<NodePostgresTransa
|
|
|
199
244
|
}
|
|
200
245
|
}
|
|
201
246
|
|
|
202
|
-
|
|
203
|
-
#provider: NodePGDSTH;
|
|
247
|
+
#provider: NodePostgresTransactionHandler;
|
|
204
248
|
|
|
205
|
-
constructor(
|
|
206
|
-
|
|
207
|
-
|
|
249
|
+
constructor(
|
|
250
|
+
readonly name: string,
|
|
251
|
+
config: PoolConfig,
|
|
252
|
+
) {
|
|
253
|
+
this.#provider = new NodePostgresTransactionHandler(name, config);
|
|
208
254
|
registerDataSource(this.#provider);
|
|
209
255
|
}
|
|
210
256
|
|
|
211
|
-
async runTransaction<T>(
|
|
212
|
-
return await runTransaction(
|
|
257
|
+
async runTransaction<T>(func: () => Promise<T>, config?: NodePostgresTransactionOptions) {
|
|
258
|
+
return await runTransaction(func, config?.name ?? func.name, { dsName: this.name, config });
|
|
213
259
|
}
|
|
214
260
|
|
|
215
261
|
registerTransaction<This, Args extends unknown[], Return>(
|
|
216
262
|
func: (this: This, ...args: Args) => Promise<Return>,
|
|
217
|
-
name: string,
|
|
218
263
|
config?: NodePostgresTransactionOptions,
|
|
219
264
|
): (this: This, ...args: Args) => Promise<Return> {
|
|
220
|
-
return registerTransaction(this.name, func, { name }, config);
|
|
265
|
+
return registerTransaction(this.name, func, { name: config?.name ?? func.name }, config);
|
|
221
266
|
}
|
|
222
267
|
|
|
223
268
|
transaction(config?: NodePostgresTransactionOptions) {
|
|
@@ -225,14 +270,17 @@ export class NodePostgresDataSource implements DBOSDataSource<NodePostgresTransa
|
|
|
225
270
|
const ds = this;
|
|
226
271
|
return function decorator<This, Args extends unknown[], Return>(
|
|
227
272
|
_target: object,
|
|
228
|
-
propertyKey:
|
|
273
|
+
propertyKey: PropertyKey,
|
|
229
274
|
descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>,
|
|
230
275
|
) {
|
|
231
276
|
if (!descriptor.value) {
|
|
232
277
|
throw Error('Use of decorator when original method is undefined');
|
|
233
278
|
}
|
|
234
279
|
|
|
235
|
-
descriptor.value = ds.registerTransaction(descriptor.value,
|
|
280
|
+
descriptor.value = ds.registerTransaction(descriptor.value, {
|
|
281
|
+
...config,
|
|
282
|
+
name: config?.name ?? String(propertyKey),
|
|
283
|
+
});
|
|
236
284
|
|
|
237
285
|
return descriptor;
|
|
238
286
|
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbos-inc/node-pg-datasource",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "3.0.16-preview",
|
|
4
|
+
"description": "DBOS DataSource library for Node-Postgres database client",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"homepage": "https://docs.dbos.dev/",
|
|
6
9
|
"repository": {
|
|
7
10
|
"type": "git",
|
|
8
11
|
"url": "https://github.com/dbos-inc/dbos-transact-ts",
|
|
9
|
-
"directory": "packages/
|
|
12
|
+
"directory": "packages/nodepg-datasource"
|
|
10
13
|
},
|
|
11
|
-
"homepage": "https://docs.dbos.dev/",
|
|
12
|
-
"main": "index.js",
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "tsc --project tsconfig.json",
|
|
15
16
|
"test": "jest --detectOpenHandles"
|