@donkeylabs/server 2.0.30 → 2.0.31

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/docs/workflows.md CHANGED
@@ -89,6 +89,19 @@ const instanceId = await ctx.core.workflows.start("process-order", {
89
89
  });
90
90
  ```
91
91
 
92
+ ### Concurrency Guard
93
+
94
+ Limit concurrent instances per workflow name:
95
+
96
+ ```ts
97
+ const server = new AppServer({
98
+ db,
99
+ workflows: {
100
+ concurrentWorkflows: 1, // 0 = unlimited
101
+ },
102
+ });
103
+ ```
104
+
92
105
  ### 3. Track Progress
93
106
 
94
107
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donkeylabs/server",
3
- "version": "2.0.30",
3
+ "version": "2.0.31",
4
4
  "type": "module",
5
5
  "description": "Type-safe plugin system for building RPC-style APIs with Bun",
6
6
  "main": "./src/index.ts",
@@ -766,6 +766,8 @@ export interface WorkflowsConfig {
766
766
  sqlitePragmas?: SqlitePragmaConfig;
767
767
  /** Disable in-process watchdog timers (use external watchdog instead) */
768
768
  useWatchdog?: boolean;
769
+ /** Max concurrent instances per workflow name (0 = unlimited, default: 0) */
770
+ concurrentWorkflows?: number;
769
771
  /** Resume strategy for orphaned workflows (default: "blocking") */
770
772
  resumeStrategy?: WorkflowResumeStrategy;
771
773
  }
@@ -871,6 +873,7 @@ class WorkflowsImpl implements Workflows {
871
873
  private killGraceMs: number;
872
874
  private sqlitePragmas?: SqlitePragmaConfig;
873
875
  private useWatchdog: boolean;
876
+ private concurrentWorkflows: number;
874
877
  private resumeStrategy!: WorkflowResumeStrategy;
875
878
  private workflowModulePaths = new Map<string, string>();
876
879
  private isolatedProcesses = new Map<string, IsolatedProcessInfo>();
@@ -908,6 +911,7 @@ class WorkflowsImpl implements Workflows {
908
911
  this.killGraceMs = config.killGraceMs ?? 5000;
909
912
  this.sqlitePragmas = config.sqlitePragmas;
910
913
  this.useWatchdog = config.useWatchdog ?? false;
914
+ this.concurrentWorkflows = config.concurrentWorkflows ?? 0;
911
915
  this.resumeStrategy = config.resumeStrategy ?? "blocking";
912
916
  }
913
917
 
@@ -1006,6 +1010,16 @@ class WorkflowsImpl implements Workflows {
1006
1010
  throw new Error(`Workflow "${workflowName}" is not registered`);
1007
1011
  }
1008
1012
 
1013
+ if (this.concurrentWorkflows > 0) {
1014
+ const running = await this.adapter.getInstancesByWorkflow(workflowName, "running");
1015
+ const pending = await this.adapter.getInstancesByWorkflow(workflowName, "pending");
1016
+ if (running.length + pending.length >= this.concurrentWorkflows) {
1017
+ throw new Error(
1018
+ `Workflow "${workflowName}" has reached its concurrency limit (${this.concurrentWorkflows})`
1019
+ );
1020
+ }
1021
+ }
1022
+
1009
1023
  const instance = await this.adapter.createInstance({
1010
1024
  workflowName,
1011
1025
  status: "pending",