@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 CHANGED
@@ -1,14 +1,18 @@
1
- import { PGTransactionConfig as NodePostgresTransactionOptions, DBOSDataSource } from '@dbos-inc/dbos-sdk/datasource';
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>(callback: () => Promise<T>, funcName: string, config?: NodePostgresTransactionOptions): Promise<T>;
11
- registerTransaction<This, Args extends unknown[], Return>(func: (this: This, ...args: Args) => Promise<Return>, name: string, config?: NodePostgresTransactionOptions): (this: This, ...args: Args) => Promise<Return>;
12
- transaction(config?: NodePostgresTransactionOptions): <This, Args extends unknown[], Return>(_target: object, propertyKey: string, descriptor: TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>) => TypedPropertyDescriptor<(this: This, ...args: Args) => Promise<Return>>;
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EAQL,mBAAmB,IAAI,8BAA8B,EACrD,cAAc,EAEf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,YAAY,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAQvF,OAAO,EAAE,8BAA8B,EAAE,CAAC;AA2J1C,qBAAa,sBAAuB,YAAW,cAAc,CAAC,8BAA8B,CAAC;;IAC3F,MAAM,KAAK,MAAM,IAAI,UAAU,CAS9B;WAEY,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAGV,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU;IAMtC,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,8BAA8B;IAI7G,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,IAAI,EAAE,MAAM,EACZ,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,MAAM,cACP,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,QAAQ,MAAM,CAAC,CAAC,oCAAxC,IAAI,WAAW,IAAI,KAAK,QAAQ,MAAM,CAAC;CAWvF"}
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 NodePGDSTH {
11
+ class NodePostgresTransactionHandler {
12
12
  name;
13
+ config;
13
14
  dsType = 'NodePostgresDataSource';
14
- #pool;
15
+ #poolField;
15
16
  constructor(name, config) {
16
17
  this.name = name;
17
- this.#pool = new pg_1.Pool(config);
18
+ this.config = config;
18
19
  }
19
- initialize() {
20
- return Promise.resolve();
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
- return this.#pool.end();
25
+ async destroy() {
26
+ const pool = this.#poolField;
27
+ this.#poolField = undefined;
28
+ await pool?.end();
24
29
  }
25
- static async #checkExecution(client, workflowID, functionNum) {
26
- const { rows } = await client.query(
27
- /*sql*/ `SELECT output FROM dbos.transaction_completion
28
- WHERE workflow_id = $1 AND function_num = $2`, [workflowID, functionNum]);
29
- return rows.length > 0 ? { output: rows[0].output } : undefined;
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, functionNum, output) {
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, functionNum, output]);
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
- if (workflowID === undefined) {
69
- throw new Error('Workflow ID is not set.');
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 (!readOnly && workflowID) {
93
- await NodePGDSTH.#recordOutput(client, workflowID, functionNum, superjson_1.SuperJSON.stringify(result));
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
- }, { isolationLevel: config?.isolationLevel, readOnly: config?.readOnly });
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
- // TODO: span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
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
- // TODO: span.setStatus({ code: SpanStatusCode.ERROR, message: e.message });
114
- // TODO: this.tracer.endSpan(span);
115
- // TODO: currently, we are *not* recording errors in the txOutput table.
116
- // For normal execution, this is fine because we also store tx step results (output and error) in the sysdb operation output table.
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
- static get client() {
153
+ name;
154
+ static #getClient(p) {
127
155
  if (!dbos_sdk_1.DBOS.isInTransaction()) {
128
- throw new Error('invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
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('No async local context found.');
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 async initializeInternalSchema(config) {
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 NodePGDSTH(name, config);
187
+ this.#provider = new NodePostgresTransactionHandler(name, config);
152
188
  (0, datasource_1.registerDataSource)(this.#provider);
153
189
  }
154
- async runTransaction(callback, funcName, config) {
155
- return await (0, datasource_1.runTransaction)(callback, funcName, { dsName: this.name, config });
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, name, config) {
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, propertyKey.toString(), config);
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":"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"}
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 as NodePostgresTransactionOptions,
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 NodePGDSTH implements DataSourceTransactionHandler {
29
- readonly name: string;
33
+ class NodePostgresTransactionHandler implements DataSourceTransactionHandler {
30
34
  readonly dsType = 'NodePostgresDataSource';
31
- readonly #pool: Pool;
35
+ #poolField: Pool | undefined;
36
+
37
+ constructor(
38
+ readonly name: string,
39
+ private readonly config: PoolConfig,
40
+ ) {}
32
41
 
33
- constructor(name: string, config: PoolConfig) {
34
- this.name = name;
35
- this.#pool = new Pool(config);
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
- initialize(): Promise<void> {
39
- return Promise.resolve();
48
+ async destroy(): Promise<void> {
49
+ const pool = this.#poolField;
50
+ this.#poolField = undefined;
51
+ await pool?.end();
40
52
  }
41
53
 
42
- destroy(): Promise<void> {
43
- return this.#pool.end();
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
- static async #checkExecution(
47
- client: ClientBase,
61
+ async #checkExecution(
48
62
  workflowID: string,
49
- functionNum: number,
50
- ): Promise<{ output: string | null } | undefined> {
51
- const { rows } = await client.query<{ output: string }>(
52
- /*sql*/ `SELECT output FROM dbos.transaction_completion
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, functionNum],
70
+ [workflowID, stepID],
55
71
  );
56
- return rows.length > 0 ? { output: rows[0].output } : undefined;
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
- functionNum: number,
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, functionNum, output],
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
- if (workflowID === undefined) {
111
- throw new Error('Workflow ID is not set.');
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
- async (client) => {
127
- // Check to see if this tx has already been executed
128
- const previousResult =
129
- readOnly || !workflowID ? undefined : await NodePGDSTH.#checkExecution(client, workflowID, functionNum);
130
- if (previousResult) {
131
- return (previousResult.output ? SuperJSON.parse(previousResult.output) : null) as Return;
132
- }
133
-
134
- // execute user's transaction function
135
- const result = await asyncLocalCtx.run({ client }, async () => {
136
- return (await func.call(target, ...args)) as Return;
137
- });
138
-
139
- // save the output of read/write transactions
140
- if (!readOnly && workflowID) {
141
- await NodePGDSTH.#recordOutput(client, workflowID, functionNum, SuperJSON.stringify(result));
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
- // TODO: span.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMillis }, performance.now());
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
- // TODO: span.setStatus({ code: SpanStatusCode.ERROR, message: e.message });
165
- // TODO: this.tracer.endSpan(span);
166
-
167
- // TODO: currently, we are *not* recording errors in the txOutput table.
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 get client(): ClientBase {
214
+ static #getClient(p?: NodePostgresTransactionHandler): ClientBase {
181
215
  if (!DBOS.isInTransaction()) {
182
- throw new Error('invalid use of NodePostgresDataSource.client outside of a DBOS transaction.');
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('No async local context found.');
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 async initializeInternalSchema(config: ClientConfig): Promise<void> {
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
- readonly name: string;
203
- #provider: NodePGDSTH;
247
+ #provider: NodePostgresTransactionHandler;
204
248
 
205
- constructor(name: string, config: PoolConfig) {
206
- this.name = name;
207
- this.#provider = new NodePGDSTH(name, config);
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>(callback: () => Promise<T>, funcName: string, config?: NodePostgresTransactionOptions) {
212
- return await runTransaction(callback, funcName, { dsName: this.name, config });
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: string,
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, propertyKey.toString(), config);
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.11-preview.gc9233b8190",
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/knex-datasource"
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"