@donkeylabs/server 2.0.31 → 2.0.32
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 +8 -1
- package/package.json +1 -1
- package/src/core/workflows.ts +26 -4
package/docs/workflows.md
CHANGED
|
@@ -97,9 +97,16 @@ Limit concurrent instances per workflow name:
|
|
|
97
97
|
const server = new AppServer({
|
|
98
98
|
db,
|
|
99
99
|
workflows: {
|
|
100
|
-
concurrentWorkflows: 1, // 0 = unlimited
|
|
100
|
+
concurrentWorkflows: 1, // default for all workflows (0 = unlimited)
|
|
101
|
+
concurrentWorkflowsByName: {
|
|
102
|
+
testWorkflow: 1,
|
|
103
|
+
ingestionWorkflow: 1,
|
|
104
|
+
},
|
|
101
105
|
},
|
|
102
106
|
});
|
|
107
|
+
|
|
108
|
+
// Or per-register override
|
|
109
|
+
ctx.core.workflows.register(orderWorkflow, { maxConcurrent: 1 });
|
|
103
110
|
```
|
|
104
111
|
|
|
105
112
|
### 3. Track Progress
|
package/package.json
CHANGED
package/src/core/workflows.ts
CHANGED
|
@@ -766,8 +766,10 @@ export interface WorkflowsConfig {
|
|
|
766
766
|
sqlitePragmas?: SqlitePragmaConfig;
|
|
767
767
|
/** Disable in-process watchdog timers (use external watchdog instead) */
|
|
768
768
|
useWatchdog?: boolean;
|
|
769
|
-
/**
|
|
769
|
+
/** Default max concurrent instances per workflow name (0 = unlimited, default: 0) */
|
|
770
770
|
concurrentWorkflows?: number;
|
|
771
|
+
/** Per-workflow concurrency overrides */
|
|
772
|
+
concurrentWorkflowsByName?: Record<string, number>;
|
|
771
773
|
/** Resume strategy for orphaned workflows (default: "blocking") */
|
|
772
774
|
resumeStrategy?: WorkflowResumeStrategy;
|
|
773
775
|
}
|
|
@@ -796,6 +798,8 @@ export interface WorkflowRegisterOptions {
|
|
|
796
798
|
* workflows.register(myWorkflow, { modulePath: import.meta.url });
|
|
797
799
|
*/
|
|
798
800
|
modulePath?: string;
|
|
801
|
+
/** Max concurrent instances for this workflow (overrides defaults) */
|
|
802
|
+
maxConcurrent?: number;
|
|
799
803
|
}
|
|
800
804
|
|
|
801
805
|
export interface Workflows {
|
|
@@ -874,6 +878,8 @@ class WorkflowsImpl implements Workflows {
|
|
|
874
878
|
private sqlitePragmas?: SqlitePragmaConfig;
|
|
875
879
|
private useWatchdog: boolean;
|
|
876
880
|
private concurrentWorkflows: number;
|
|
881
|
+
private concurrentWorkflowsByName: Record<string, number>;
|
|
882
|
+
private workflowConcurrencyOverrides = new Map<string, number>();
|
|
877
883
|
private resumeStrategy!: WorkflowResumeStrategy;
|
|
878
884
|
private workflowModulePaths = new Map<string, string>();
|
|
879
885
|
private isolatedProcesses = new Map<string, IsolatedProcessInfo>();
|
|
@@ -912,6 +918,7 @@ class WorkflowsImpl implements Workflows {
|
|
|
912
918
|
this.sqlitePragmas = config.sqlitePragmas;
|
|
913
919
|
this.useWatchdog = config.useWatchdog ?? false;
|
|
914
920
|
this.concurrentWorkflows = config.concurrentWorkflows ?? 0;
|
|
921
|
+
this.concurrentWorkflowsByName = config.concurrentWorkflowsByName ?? {};
|
|
915
922
|
this.resumeStrategy = config.resumeStrategy ?? "blocking";
|
|
916
923
|
}
|
|
917
924
|
|
|
@@ -1002,6 +1009,10 @@ class WorkflowsImpl implements Workflows {
|
|
|
1002
1009
|
}
|
|
1003
1010
|
|
|
1004
1011
|
this.definitions.set(definition.name, definition);
|
|
1012
|
+
|
|
1013
|
+
if (options?.maxConcurrent !== undefined) {
|
|
1014
|
+
this.workflowConcurrencyOverrides.set(definition.name, options.maxConcurrent);
|
|
1015
|
+
}
|
|
1005
1016
|
}
|
|
1006
1017
|
|
|
1007
1018
|
async start<T = any>(workflowName: string, input: T): Promise<string> {
|
|
@@ -1010,12 +1021,13 @@ class WorkflowsImpl implements Workflows {
|
|
|
1010
1021
|
throw new Error(`Workflow "${workflowName}" is not registered`);
|
|
1011
1022
|
}
|
|
1012
1023
|
|
|
1013
|
-
|
|
1024
|
+
const limit = this.getConcurrencyLimit(workflowName);
|
|
1025
|
+
if (limit > 0) {
|
|
1014
1026
|
const running = await this.adapter.getInstancesByWorkflow(workflowName, "running");
|
|
1015
1027
|
const pending = await this.adapter.getInstancesByWorkflow(workflowName, "pending");
|
|
1016
|
-
if (running.length + pending.length >=
|
|
1028
|
+
if (running.length + pending.length >= limit) {
|
|
1017
1029
|
throw new Error(
|
|
1018
|
-
`Workflow "${workflowName}" has reached its concurrency limit (${
|
|
1030
|
+
`Workflow "${workflowName}" has reached its concurrency limit (${limit})`
|
|
1019
1031
|
);
|
|
1020
1032
|
}
|
|
1021
1033
|
}
|
|
@@ -2005,6 +2017,16 @@ class WorkflowsImpl implements Workflows {
|
|
|
2005
2017
|
await this.adapter.updateInstance(instanceId, { metadata });
|
|
2006
2018
|
}
|
|
2007
2019
|
|
|
2020
|
+
private getConcurrencyLimit(workflowName: string): number {
|
|
2021
|
+
if (this.workflowConcurrencyOverrides.has(workflowName)) {
|
|
2022
|
+
return this.workflowConcurrencyOverrides.get(workflowName) ?? 0;
|
|
2023
|
+
}
|
|
2024
|
+
if (this.concurrentWorkflowsByName[workflowName] !== undefined) {
|
|
2025
|
+
return this.concurrentWorkflowsByName[workflowName] ?? 0;
|
|
2026
|
+
}
|
|
2027
|
+
return this.concurrentWorkflows;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2008
2030
|
private async markOrphanedAsFailed(
|
|
2009
2031
|
instances: WorkflowInstance[],
|
|
2010
2032
|
reason: string
|