@dbos-inc/node-pg-datasource 5.2.4-preview → 5.2.6-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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAUL,mBAAmB,EACnB,cAAc,EAIf,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;AA4O1C,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,EAAE,UAAU,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;gBAcxF,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,UAAU,EAClB,UAAU,GAAE,MAAe;IAMvB,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,GAAG,YAAY,GACrD,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC;IAIjD,WAAW,CAAC,MAAM,CAAC,EAAE,8BAA8B,kDAIvC,MAAM,eACD,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;CAevF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAYL,mBAAmB,EACnB,cAAc,EAIf,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;AAmP1C,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,EAAE,UAAU,GAAE,MAAe,GAAG,OAAO,CAAC,IAAI,CAAC;gBAcxF,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,UAAU,EAClB,UAAU,GAAE,MAAe;IAMvB,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,GAAG,YAAY,GACrD,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC;IAIjD,WAAW,CAAC,MAAM,CAAC,EAAE,8BAA8B,kDAIvC,MAAM,eACD,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;CAevF"}
package/dist/index.js CHANGED
@@ -59,11 +59,14 @@ class NodePostgresTransactionHandler {
59
59
  }
60
60
  return this.#poolField;
61
61
  }
62
- async deleteCheckpoints(workflowID, startStep) {
63
- await this.#pool.query(
64
- /*sql*/
65
- `DELETE FROM "${this.schemaName}".transaction_completion
66
- WHERE workflow_id = $1 AND function_num >= $2`, [workflowID, startStep]);
62
+ async deleteCheckpoints(workflowID, startStep, beforeCommit) {
63
+ await this.#transaction(async (client) => {
64
+ await client.query(
65
+ /*sql*/
66
+ `DELETE FROM "${this.schemaName}".transaction_completion
67
+ WHERE workflow_id = $1 AND function_num >= $2`, [workflowID, startStep]);
68
+ await beforeCommit?.();
69
+ });
67
70
  }
68
71
  async #checkExecution(workflowID, stepID) {
69
72
  const { rows } = await this.#pool.query(
@@ -86,6 +89,8 @@ class NodePostgresTransactionHandler {
86
89
  if (rows.length === 0) {
87
90
  throw new datasource_1.DBOSStepAlreadyRecordedError(workflowID, stepID);
88
91
  }
92
+ // Holding this step's row, so a later owner's insert waits on our commit.
93
+ await (0, datasource_1.assertStillOwnsWorkflow)(workflowID);
89
94
  }
90
95
  async #recordError(workflowID, stepID, error) {
91
96
  const { rows } = await this.#pool.query(
@@ -161,6 +166,9 @@ class NodePostgresTransactionHandler {
161
166
  if (saveResults && error instanceof datasource_1.DBOSStepAlreadyRecordedError) {
162
167
  return await this.#replayConflictingStep(workflowID, stepID);
163
168
  }
169
+ // The new owner wins; recording an error here would replay it as this step's outcome.
170
+ if (error instanceof datasource_1.DBOSWorkflowConflictError)
171
+ throw error;
164
172
  if ((0, datasource_1.isPGRetriableTransactionError)(error)) {
165
173
  dbos_sdk_1.DBOS.span?.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMS }, performance.now());
166
174
  await new Promise((resolve) => setTimeout(resolve, retryWaitMS));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAAA,gDAAgD;;;AAEhD,iDAAwD;AACxD,8DAeuC;AACvC,2BAAuF;AACvF,uDAAqD;AACrD,yCAAsC;AAatC,MAAM,aAAa,GAAG,IAAI,oCAAiB,EAAiC,CAAC;AAE7E,MAAM,8BAA8B;IAKvB;IACQ;IALnB,UAAU,CAAmB;IACpB,UAAU,CAAS;IAE5B,YACW,IAAY,EACJ,MAAkB,EACnC,aAAqB,MAAM;QAFlB,SAAI,GAAJ,IAAI,CAAQ;QACJ,WAAM,GAAN,MAAM,CAAY;QAGnC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,SAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,IAAI,EAAE,GAAG,EAAE,CAAC;QAElB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAE/C,IAAI,CAAC;YACH,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAAgC,IAAA,sCAAyB,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC1G,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;YACxE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CACb,iDAAiD,IAAI,CAAC,IAAI,oCAAqC,CAAW,CAAC,OAAO,EAAE,CACrH,CAAC;YACJ,CAAC;YAED,UAAU;YACV,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,KAAK,CAAC,IAAA,gDAAmC,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;oBACzE,MAAM,MAAM,CAAC,KAAK,CAAC,IAAA,+CAAkC,EAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC1E,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CACb,iDAAiD,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC,UAAU;4GACa,CACjG,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,MAAM,IAAI,EAAE,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,IAAI,KAAK;QACP,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,sBAAsB,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;QAC3D,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK;QACpB,OAAO;QACP,gBAAgB,IAAI,CAAC,UAAU;qDACgB,EAC/C,CAAC,UAAU,EAAE,SAAS,CAAC,CACxB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,UAAkB,EAClB,MAAc;QAGd,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK;QACrC,OAAO;QACP,8BAA8B,IAAI,CAAC,UAAU;oDACC,EAC9C,CAAC,UAAU,EAAE,MAAM,CAAC,CACrB,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,aAAa,CACxB,MAAkB,EAClB,UAAkB,EAClB,MAAc,EACd,MAAqB,EACrB,UAAkB;QAElB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK;QACjC,OAAO;QACP,gBAAgB,UAAU;;;6BAGH,EACvB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAC7B,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,yCAA4B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,MAAc,EAAE,KAAa;QAClE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK;QACrC,OAAO;QACP,gBAAgB,IAAI,CAAC,UAAU;;;6BAGR,EACvB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,CAC5B,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,yCAA4B,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,KAAK,CAAC,sBAAsB,CAAS,UAAkB,EAAE,MAAc;QACrE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAChE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,sBAAS,CACjB,QAAQ,MAAM,gBAAgB,UAAU,4EAA4E,CACrH,CAAC;QACJ,CAAC;QACD,OAAO,IAAA,+BAAkB,EAAS,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAA6C,EAC7C,SAAyC,EAAE;QAE3C,MAAM,cAAc,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,mBAAmB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC;QAC3C,MAAM,UAAU,GAAG,MAAM,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC;QAE/F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,cAAc,IAAI,UAAU,EAAE,CAAC,CAAC;YACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACrC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACvC,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,yBAAyB,CAC7B,MAAkD,EAClD,MAAY,EACZ,IAAoD,EACpD,GAAG,IAAU;QAEb,MAAM,UAAU,GAAG,eAAI,CAAC,UAAU,CAAC;QACnC,MAAM,MAAM,GAAG,eAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,UAAU,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC;QAC3C,MAAM,WAAW,GAAG,CAAC,QAAQ,IAAI,UAAU,KAAK,SAAS,CAAC;QAE1D,4BAA4B;QAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,MAAM,aAAa,GAAG,GAAG,CAAC;QAC1B,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,0BAA0B;QAEvD,OAAO,IAAI,EAAE,CAAC;YACZ,oDAAoD;YACpD,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,MAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACjG,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,IAAA,+BAAkB,EAAS,cAAc,CAAC,CAAC;YACpD,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAS,KAAK,EAAE,MAAM,EAAE,EAAE;oBAC9D,sCAAsC;oBACtC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE;wBACzE,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAW,CAAC;oBACtD,CAAC,CAAC,CAAC;oBAEH,6CAA6C;oBAC7C,IAAI,WAAW,EAAE,CAAC;wBAChB,MAAM,8BAA8B,CAAC,aAAa,CAChD,MAAM,EACN,UAAU,EACV,MAAO,EACP,qBAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAC3B,IAAI,CAAC,UAAU,CAChB,CAAC;oBACJ,CAAC;oBAED,OAAO,MAAM,CAAC;gBAChB,CAAC,EAAE,MAAM,CAAC,CAAC;gBAEX,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,WAAW,IAAI,KAAK,YAAY,yCAA4B,EAAE,CAAC;oBACjE,OAAO,MAAM,IAAI,CAAC,sBAAsB,CAAS,UAAU,EAAE,MAAO,CAAC,CAAC;gBACxE,CAAC;gBACD,IAAI,IAAA,0CAA6B,EAAC,KAAK,CAAC,EAAE,CAAC;oBACzC,eAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,2BAA2B,EAAE,EAAE,eAAe,EAAE,WAAW,EAAE,EAAE,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;oBACtG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;oBACjE,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,aAAa,EAAE,cAAc,CAAC,CAAC;oBACpE,SAAS;gBACX,CAAC;qBAAM,CAAC;oBACN,IAAI,WAAW,EAAE,CAAC;wBAChB,MAAM,OAAO,GAAG,qBAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;wBAC3C,IAAI,CAAC;4BACH,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,MAAO,EAAE,OAAO,CAAC,CAAC;wBACxD,CAAC;wBAAC,OAAO,WAAW,EAAE,CAAC;4BACrB,IAAI,WAAW,YAAY,yCAA4B,EAAE,CAAC;gCACxD,OAAO,MAAM,IAAI,CAAC,sBAAsB,CAAS,UAAU,EAAE,MAAO,CAAC,CAAC;4BACxE,CAAC;4BACD,MAAM,WAAW,CAAC;wBACpB,CAAC;oBACH,CAAC;oBAED,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAa,sBAAsB;IAqCtB;IApCX,MAAM,CAAC,UAAU,CAAC,CAAkC;QAClD,IAAI,CAAC,eAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACjG,CAAC;QACD,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACjG,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;IAED,MAAM,KAAK,MAAM;QACf,OAAO,sBAAsB,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,MAAM;QACR,OAAO,sBAAsB,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAoB,EAAE,aAAqB,MAAM;QACjF,MAAM,MAAM,GAAG,IAAI,WAAM,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,MAAM,CAAC,KAAK,CAAC,IAAA,gDAAmC,EAAC,UAAU,CAAC,CAAC,CAAC;YACpE,MAAM,MAAM,CAAC,KAAK,CAAC,IAAA,+CAAkC,EAAC,UAAU,CAAC,CAAC,CAAC;QACrE,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,SAAS,CAAiC;IAE1C,YACW,IAAY,EACrB,MAAkB,EAClB,aAAqB,MAAM;QAFlB,SAAI,GAAJ,IAAI,CAAQ;QAIrB,IAAI,CAAC,SAAS,GAAG,IAAI,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAC9E,IAAA,+BAAkB,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,cAAc,CAAI,IAAsB,EAAE,MAAuC;QACrF,OAAO,MAAM,IAAA,2BAAc,EAAC,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,mBAAmB,CACjB,IAAoD,EACpD,MAAsD;QAEtD,OAAO,IAAA,gCAAmB,EAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,WAAW,CAAC,MAAuC;QACjD,4DAA4D;QAC5D,MAAM,EAAE,GAAG,IAAI,CAAC;QAChB,OAAO,SAAS,SAAS,CACvB,MAAc,EACd,WAAwB,EACxB,UAAmF;YAEnF,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACpE,CAAC;YAED,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,mBAAmB,CAAC,UAAU,CAAC,KAAK,EAAE;gBAC1D,GAAG,MAAM;gBACT,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,MAAM,CAAC,WAAW,CAAC;gBACzC,WAAW,EAAE,MAAM;aACpB,CAAC,CAAC;YAEH,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC;IACJ,CAAC;CACF;AA7ED,wDA6EC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
package/index.ts CHANGED
@@ -8,6 +8,8 @@ import {
8
8
  isPGRetriableTransactionError,
9
9
  DBOSError,
10
10
  DBOSStepAlreadyRecordedError,
11
+ DBOSWorkflowConflictError,
12
+ assertStillOwnsWorkflow,
11
13
  replayRecordedStep,
12
14
  registerTransaction,
13
15
  runTransaction,
@@ -94,13 +96,16 @@ class NodePostgresTransactionHandler implements DataSourceTransactionHandler {
94
96
  return this.#poolField;
95
97
  }
96
98
 
97
- async deleteCheckpoints(workflowID: string, startStep: number): Promise<void> {
98
- await this.#pool.query(
99
- /*sql*/
100
- `DELETE FROM "${this.schemaName}".transaction_completion
101
- WHERE workflow_id = $1 AND function_num >= $2`,
102
- [workflowID, startStep],
103
- );
99
+ async deleteCheckpoints(workflowID: string, startStep: number, beforeCommit?: () => Promise<void>): Promise<void> {
100
+ await this.#transaction(async (client) => {
101
+ await client.query(
102
+ /*sql*/
103
+ `DELETE FROM "${this.schemaName}".transaction_completion
104
+ WHERE workflow_id = $1 AND function_num >= $2`,
105
+ [workflowID, startStep],
106
+ );
107
+ await beforeCommit?.();
108
+ });
104
109
  }
105
110
 
106
111
  async #checkExecution(
@@ -139,6 +144,8 @@ class NodePostgresTransactionHandler implements DataSourceTransactionHandler {
139
144
  if (rows.length === 0) {
140
145
  throw new DBOSStepAlreadyRecordedError(workflowID, stepID);
141
146
  }
147
+ // Holding this step's row, so a later owner's insert waits on our commit.
148
+ await assertStillOwnsWorkflow(workflowID);
142
149
  }
143
150
 
144
151
  async #recordError(workflowID: string, stepID: number, error: string): Promise<void> {
@@ -241,6 +248,8 @@ class NodePostgresTransactionHandler implements DataSourceTransactionHandler {
241
248
  if (saveResults && error instanceof DBOSStepAlreadyRecordedError) {
242
249
  return await this.#replayConflictingStep<Return>(workflowID, stepID!);
243
250
  }
251
+ // The new owner wins; recording an error here would replay it as this step's outcome.
252
+ if (error instanceof DBOSWorkflowConflictError) throw error;
244
253
  if (isPGRetriableTransactionError(error)) {
245
254
  DBOS.span?.addEvent('TXN SERIALIZATION FAILURE', { retryWaitMillis: retryWaitMS }, performance.now());
246
255
  await new Promise((resolve) => setTimeout(resolve, retryWaitMS));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbos-inc/node-pg-datasource",
3
- "version": "5.2.4-preview",
3
+ "version": "5.2.6-preview",
4
4
  "description": "DBOS DataSource library for Node-Postgres database client",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -1,4 +1,4 @@
1
- import { DBOS } from '@dbos-inc/dbos-sdk';
1
+ import { DBOS, DBOSWorkflowConflictError } from '@dbos-inc/dbos-sdk';
2
2
  import { Client, Pool } from 'pg';
3
3
  import { NodePostgresDataSource } from '..';
4
4
  import { dropDB, ensureDB } from './test-helpers';
@@ -50,34 +50,39 @@ describe('NodePostgresDataSource', () => {
50
50
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
51
51
  [workflowID],
52
52
  );
53
- expect(rows.length).toBe(1);
54
- expect(rows[0].workflow_id).toBe(workflowID);
55
- expect(rows[0].function_num).toBe(0);
56
- expect(rows[0].output).not.toBeNull();
57
- expect(SuperJSON.parse(rows[0].output!)).toMatchObject({ user, greet_count: 1 });
53
+ // Completion cleared the checkpoint the transaction wrote.
54
+ expect(rows).toHaveLength(0);
58
55
  });
59
56
 
60
57
  test('rewind drops the checkpoints past the cut', async () => {
61
58
  const user = 'rewindTest';
62
59
  await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
60
+ rewindState = newRewindState();
63
61
  const workflowID = randomUUID();
64
62
 
65
- await expect(DBOS.withNextWorkflowID(workflowID, () => regTwoInsertWorkflow(user))).resolves.toEqual([1, 2]);
66
- const before = await completions(workflowID);
67
- expect(before.map((r) => r.function_num)).toEqual([0, 1]);
63
+ // A cancelled workflow keeps its checkpoints, so the rewind has some to cut.
64
+ const execution = DBOS.withNextWorkflowID(workflowID, () => regGatedTwoInsertWorkflow(user));
65
+ await Promise.race([rewindState.atGate.opened, execution]);
66
+ await DBOS.cancelWorkflow(workflowID);
67
+ rewindState.release.open();
68
+ await expect(execution).rejects.toThrow(/has been cancelled/);
69
+ expect((await completions(workflowID)).map((r) => r.function_num)).toEqual([0, 1]);
70
+
71
+ // Hold the queue's only worker so the rewound workflow waits while its checkpoints are inspected.
72
+ await DBOS.registerQueue('nodepg_rewind_gate', { workerConcurrency: 1, onConflict: 'always_update' });
73
+ const blocker = await DBOS.startWorkflow(regQueueBlocker, { queueName: 'nodepg_rewind_gate' })();
74
+ await rewindState.blockerStarted.opened;
75
+ const handle = await DBOS.rewindWorkflow<number[]>(workflowID, {
76
+ startStep: 1,
77
+ queueName: 'nodepg_rewind_gate',
78
+ });
79
+ expect((await completions(workflowID)).map((r) => r.function_num)).toEqual([0]);
68
80
 
69
- // Cut at the second transaction. Its checkpoint has to go with the step: left
70
- // behind, the replay would read greet_count 2 back out of it instead of running
71
- // the INSERT again, and the table would stay at 2.
72
- const handle = await DBOS.rewindWorkflow<number[]>(workflowID, { startStep: 1 });
73
- // The first transaction replays from the checkpoint that survived the cut, so it
74
- // still reports 1; the second really runs again, taking the table from 2 to 3.
81
+ // The first transaction replays and still reports 1; the second really runs again, taking the table to 3.
82
+ rewindState.unblock.open();
83
+ await blocker.getResult();
75
84
  await expect(handle.getResult()).resolves.toEqual([1, 3]);
76
-
77
- const after = await completions(workflowID);
78
- expect(after.map((r) => r.function_num)).toEqual([0, 1]);
79
- expect(SuperJSON.parse(after[0].output!)).toMatchObject({ user, greet_count: 1 });
80
- expect(SuperJSON.parse(after[1].output!)).toMatchObject({ user, greet_count: 3 });
85
+ expect(await completions(workflowID)).toHaveLength(0);
81
86
  });
82
87
 
83
88
  async function completions(workflowID: string) {
@@ -115,11 +120,8 @@ describe('NodePostgresDataSource', () => {
115
120
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
116
121
  [workflowID],
117
122
  );
118
- expect(rows.length).toBe(1);
119
- expect(rows[0].workflow_id).toBe(workflowID);
120
- expect(rows[0].function_num).toBe(0);
121
- expect(rows[0].output).not.toBeNull();
122
- expect(SuperJSON.parse(rows[0].output!)).toMatchObject({ user, greet_count: 1 });
123
+ // Completion cleared the checkpoint the transaction wrote.
124
+ expect(rows).toHaveLength(0);
123
125
  });
124
126
 
125
127
  test('rerun insert dataSource.runAsTx function', async () => {
@@ -164,14 +166,8 @@ describe('NodePostgresDataSource', () => {
164
166
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
165
167
  [workflowID],
166
168
  );
167
- expect(txOutput.length).toBe(1);
168
- expect(txOutput[0].workflow_id).toBe(workflowID);
169
- expect(txOutput[0].function_num).toBe(0);
170
- expect(txOutput[0].output).toBeNull();
171
- expect(txOutput[0].error).not.toBeNull();
172
- const $error = SuperJSON.parse(txOutput[0].error!);
173
- expect($error).toBeInstanceOf(Error);
174
- expect(($error as Error).message).toMatch(/^test error \d+$/);
169
+ // Completion cleared the checkpoint the transaction wrote.
170
+ expect(txOutput).toHaveLength(0);
175
171
  });
176
172
 
177
173
  test('rerun error dataSource.register function', async () => {
@@ -209,14 +205,8 @@ describe('NodePostgresDataSource', () => {
209
205
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
210
206
  [workflowID],
211
207
  );
212
- expect(txOutput.length).toBe(1);
213
- expect(txOutput[0].workflow_id).toBe(workflowID);
214
- expect(txOutput[0].function_num).toBe(0);
215
- expect(txOutput[0].output).toBeNull();
216
- expect(txOutput[0].error).not.toBeNull();
217
- const $error = SuperJSON.parse(txOutput[0].error!);
218
- expect($error).toBeInstanceOf(Error);
219
- expect(($error as Error).message).toMatch(/^test error \d+$/);
208
+ // Completion cleared the checkpoint the transaction wrote.
209
+ expect(txOutput).toHaveLength(0);
220
210
  });
221
211
 
222
212
  test('rerun error dataSource.runAsTx function', async () => {
@@ -359,25 +349,68 @@ describe('NodePostgresDataSource', () => {
359
349
  expect((winnerError as Error).message).toBe('winner-error');
360
350
  expect(raceState.callCount).toBe(3);
361
351
 
362
- // Every loser's writes were discarded and the winners' records still stand.
352
+ // Every loser's writes were discarded, and completion cleared the winners' checkpoints.
363
353
  const { rows: tags } = await userDB.query<{ tag: string }>('SELECT tag FROM race_side_effects');
364
354
  expect(tags).toHaveLength(0);
365
355
  const { rows: txOutput } = await userDB.query<transaction_completion>(
366
356
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = ANY($1)',
367
- [[wfid1, wfid2]],
357
+ [[wfid1, wfid2, wfid3]],
368
358
  );
369
- expect(txOutput).toHaveLength(2);
370
- for (const row of txOutput) {
371
- expect(row.error).toBeNull(); // no loser error was ever recorded
372
- expect(SuperJSON.parse(row.output!)).toBe('winner-result');
359
+ expect(txOutput).toHaveLength(0);
360
+ });
361
+
362
+ /** Completion clears the checkpoints, so the workflow reads them back while it still runs. */
363
+ test('transactions record their outcomes as checkpoints', async () => {
364
+ const user = 'checkpointProbe';
365
+ await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
366
+ const workflowID = randomUUID();
367
+ const message = await DBOS.withNextWorkflowID(workflowID, () => regCheckpointProbeWorkflow(user));
368
+ expect(message).toMatch(/^test error \d+$/);
369
+
370
+ expect(probeState.rows).toHaveLength(2);
371
+ const [output, error] = probeState.rows;
372
+ expect(output).toMatchObject({ workflow_id: workflowID, function_num: 0, error: null });
373
+ expect(SuperJSON.parse(output.output!)).toMatchObject({ user, greet_count: 1 });
374
+ expect(error).toMatchObject({ workflow_id: workflowID, function_num: 1, output: null });
375
+ expect(SuperJSON.parse<Error>(error.error!).message).toBe(message);
376
+ expect(await readCompletions(workflowID)).toHaveLength(0);
377
+ });
378
+
379
+ /** The checkpoint may be the new owner's only record of the transaction, so a stale execution's cleanup rolls back. */
380
+ test('a stale execution keeps the checkpoints at completion', async () => {
381
+ const user = 'handOffTest';
382
+ await userDB.query('DELETE FROM greetings WHERE name = $1', [user]);
383
+ const handedOff = new Promise<void>((resolve) => (handOffState.signal = resolve));
384
+ const workflowID = randomUUID();
385
+ const execution = DBOS.withNextWorkflowID(workflowID, () => regHandOffWorkflow(user));
386
+ // Surfaces the workflow's own error if it fails before handing off.
387
+ await Promise.race([handedOff, execution]);
388
+
389
+ // Its outcome is refused too, so the execution parks until the workflow is cancelled.
390
+ await DBOS.cancelWorkflow(workflowID);
391
+ await expect(execution).rejects.toThrow(/has been cancelled/);
392
+ expect(await readCompletions(workflowID)).toHaveLength(1);
393
+ });
394
+
395
+ /** A stale execution cannot apply a step the new owner also runs, nor leave an outcome for it. */
396
+ test('an execution that loses the workflow mid-transaction rolls back', async () => {
397
+ await userDB.query('DELETE FROM race_side_effects');
398
+ const workflowID = randomUUID();
399
+ await expect(DBOS.withNextWorkflowID(workflowID, () => regStaleWorkflow())).resolves.toBe('conflicted');
400
+
401
+ const { rows: tags } = await userDB.query<{ tag: string }>('SELECT tag FROM race_side_effects');
402
+ expect(tags).toHaveLength(0);
403
+ expect(staleState.completionsAtConflict).toBe(0);
404
+ const sysDB = new Client(sysConfig);
405
+ try {
406
+ await sysDB.connect();
407
+ const { rows: steps } = await sysDB.query('SELECT * FROM dbos.operation_outputs WHERE workflow_uuid = $1', [
408
+ workflowID,
409
+ ]);
410
+ expect(steps).toHaveLength(0);
411
+ } finally {
412
+ await sysDB.end();
373
413
  }
374
- const { rows: txError } = await userDB.query<transaction_completion>(
375
- 'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
376
- [wfid3],
377
- );
378
- expect(txError).toHaveLength(1);
379
- expect(txError[0].output).toBeNull(); // the loser never overwrote the winner's error with its own result
380
- expect(SuperJSON.parse<Error>(txError[0].error!).message).toBe('winner-error');
381
414
  });
382
415
  });
383
416
 
@@ -491,13 +524,127 @@ const regRaceWorkflow = DBOS.registerWorkflow(raceWorkflow);
491
524
 
492
525
  const regInsertFunction = dataSource.registerTransaction(insertFunction);
493
526
 
494
- async function twoInsertWorkflow(user: string) {
527
+ const regErrorFunction = dataSource.registerTransaction(errorFunction);
528
+
529
+ const sysConfig = { ...config, database: 'node_pg_ds_test_dbos_sys' };
530
+
531
+ /** Set the workflow's ownership token, returning the one it replaced. */
532
+ async function setOwner(workflowID: string, ownerXid: string | null): Promise<string | null> {
533
+ const sysDB = new Client(sysConfig);
534
+ try {
535
+ await sysDB.connect();
536
+ const { rows } = await sysDB.query<{ owner_xid: string | null }>(
537
+ 'SELECT owner_xid FROM dbos.workflow_status WHERE workflow_uuid = $1',
538
+ [workflowID],
539
+ );
540
+ await sysDB.query('UPDATE dbos.workflow_status SET owner_xid = $2 WHERE workflow_uuid = $1', [
541
+ workflowID,
542
+ ownerXid,
543
+ ]);
544
+ return rows[0].owner_xid;
545
+ } finally {
546
+ await sysDB.end();
547
+ }
548
+ }
549
+
550
+ /** The checkpoints this data source holds for the workflow, read on a connection of their own. */
551
+ async function readCompletions(workflowID: string): Promise<transaction_completion[]> {
552
+ const client = new Client(config);
553
+ try {
554
+ await client.connect();
555
+ const { rows } = await client.query<transaction_completion>(
556
+ 'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1 ORDER BY function_num',
557
+ [workflowID],
558
+ );
559
+ return rows;
560
+ } finally {
561
+ await client.end();
562
+ }
563
+ }
564
+
565
+ const probeState = { rows: [] as transaction_completion[] };
566
+
567
+ // Reads back the checkpoints its transactions wrote, before completion clears them.
568
+ async function checkpointProbeWorkflow(user: string) {
569
+ await regInsertFunction(user);
570
+ const error = await regErrorFunction(user).then(
571
+ () => undefined,
572
+ (e: Error) => e,
573
+ );
574
+ probeState.rows = await readCompletions(DBOS.workflowID!);
575
+ return error?.message;
576
+ }
577
+
578
+ const regCheckpointProbeWorkflow = DBOS.registerWorkflow(checkpointProbeWorkflow);
579
+
580
+ const staleState = { originalOwner: null as string | null, completionsAtConflict: -1 };
581
+
582
+ // Another execution claims the workflow while this transaction is still open.
583
+ async function staleFunction() {
584
+ await NodePostgresDataSource.client.query("INSERT INTO race_side_effects(tag) VALUES ('stale')");
585
+ staleState.originalOwner = await setOwner(DBOS.workflowID!, 'another-execution');
586
+ return 'stale';
587
+ }
588
+
589
+ const regStaleFunction = dataSource.registerTransaction(staleFunction);
590
+
591
+ async function staleWorkflow() {
592
+ try {
593
+ return await regStaleFunction();
594
+ } catch (e) {
595
+ if (!(e instanceof DBOSWorkflowConflictError)) throw e;
596
+ // Read before completion clears it: the conflict must not have been recorded as the step's outcome.
597
+ staleState.completionsAtConflict = (await readCompletions(DBOS.workflowID!)).length;
598
+ // A real duplicate parks here instead; reclaimed so this execution's outcome lands.
599
+ await setOwner(DBOS.workflowID!, staleState.originalOwner);
600
+ return 'conflicted';
601
+ }
602
+ }
603
+
604
+ const regStaleWorkflow = DBOS.registerWorkflow(staleWorkflow);
605
+
606
+ const handOffState = { signal: () => {} };
607
+
608
+ // Loses the workflow after its transaction, so its completion is a stale execution's.
609
+ async function handOffWorkflow(user: string) {
610
+ const result = await regInsertFunction(user);
611
+ await setOwner(DBOS.workflowID!, 'another-execution');
612
+ handOffState.signal();
613
+ return result;
614
+ }
615
+
616
+ const regHandOffWorkflow = DBOS.registerWorkflow(handOffWorkflow);
617
+
618
+ /** A promise with its resolver, so a test and a workflow can hand control back and forth. */
619
+ function latch() {
620
+ let open = () => {};
621
+ const opened = new Promise<void>((resolve) => (open = resolve));
622
+ return { open, opened };
623
+ }
624
+
625
+ function newRewindState() {
626
+ return { atGate: latch(), release: latch(), blockerStarted: latch(), unblock: latch() };
627
+ }
628
+
629
+ let rewindState = newRewindState();
630
+
631
+ // Waits after its transactions, so the test can cancel it with both checkpoints in place.
632
+ async function gatedTwoInsertWorkflow(user: string) {
495
633
  const first = await regInsertFunction(user);
496
634
  const second = await regInsertFunction(user);
635
+ rewindState.atGate.open();
636
+ await rewindState.release.opened;
497
637
  return [first.greet_count, second.greet_count];
498
638
  }
499
- const regTwoInsertWorkflow = DBOS.registerWorkflow(twoInsertWorkflow);
500
- const regErrorFunction = dataSource.registerTransaction(errorFunction);
639
+
640
+ const regGatedTwoInsertWorkflow = DBOS.registerWorkflow(gatedTwoInsertWorkflow);
641
+
642
+ async function queueBlocker() {
643
+ rewindState.blockerStarted.open();
644
+ await rewindState.unblock.opened;
645
+ }
646
+
647
+ const regQueueBlocker = DBOS.registerWorkflow(queueBlocker);
501
648
  const regReadFunction = dataSource.registerTransaction(readFunction, { readOnly: true });
502
649
 
503
650
  class StaticClass {
@@ -614,10 +761,7 @@ describe('NodePostgresDataSourceCreateTxC', () => {
614
761
  'SELECT * FROM dbos.transaction_completion WHERE workflow_id = $1',
615
762
  [workflowID],
616
763
  );
617
- expect(rows.length).toBe(1);
618
- expect(rows[0].workflow_id).toBe(workflowID);
619
- expect(rows[0].function_num).toBe(0);
620
- expect(rows[0].output).not.toBeNull();
621
- expect(SuperJSON.parse(rows[0].output!)).toMatchObject({ user, greet_count: 1 });
764
+ // Completion cleared the checkpoint the transaction wrote.
765
+ expect(rows).toHaveLength(0);
622
766
  });
623
767
  });