@brandboostinggmbh/observable-workflows 0.21.1 → 0.22.0
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 +42 -1
- package/dist/index.js +119 -30
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -91,6 +91,16 @@ interface PrepareWorkflowInsertParams {
|
|
|
91
91
|
* When provided, dependency insert statements are included in the returned statements array.
|
|
92
92
|
*/
|
|
93
93
|
dependencies?: Array<WorkflowDependency>;
|
|
94
|
+
/**
|
|
95
|
+
* Optional delay in seconds before the queue message is delivered.
|
|
96
|
+
* Stored in the DB for transparency so the original delay intent is preserved.
|
|
97
|
+
*/
|
|
98
|
+
delaySeconds?: number | null;
|
|
99
|
+
/**
|
|
100
|
+
* Absolute epoch-millisecond timestamp indicating the earliest time the queue message
|
|
101
|
+
* will be delivered. `null` or absent when no delay was requested.
|
|
102
|
+
*/
|
|
103
|
+
scheduledFor?: number | null;
|
|
94
104
|
}
|
|
95
105
|
/** Result from preparing workflow insert statements */
|
|
96
106
|
|
|
@@ -214,6 +224,8 @@ declare function workflowTableRowToWorkflowRun({
|
|
|
214
224
|
endTime: number | null;
|
|
215
225
|
parentInstanceId: string | null;
|
|
216
226
|
triggerId: string | null;
|
|
227
|
+
delaySeconds?: number | null;
|
|
228
|
+
scheduledFor?: number | null;
|
|
217
229
|
};
|
|
218
230
|
serializer: Serializer;
|
|
219
231
|
externalBlobStorage?: ExternalBlobStorage;
|
|
@@ -259,6 +271,8 @@ declare function updateWorkflow(context: InternalWorkflowContextOptions, instanc
|
|
|
259
271
|
startTime?: number;
|
|
260
272
|
parentInstanceId?: string | null;
|
|
261
273
|
triggerId?: string | null;
|
|
274
|
+
delaySeconds?: number | null;
|
|
275
|
+
scheduledFor?: number | null;
|
|
262
276
|
}): Promise<D1Result<Record<string, unknown>> | {
|
|
263
277
|
success: boolean;
|
|
264
278
|
meta: {
|
|
@@ -484,6 +498,22 @@ type WorkflowRun = {
|
|
|
484
498
|
isRetryOf?: WorkflowRun | null;
|
|
485
499
|
retries?: WorkflowRun[] | null;
|
|
486
500
|
properties?: WorkflowProperty[];
|
|
501
|
+
/**
|
|
502
|
+
* Optional queue delivery delay in seconds. Stored for transparency so the original
|
|
503
|
+
* delay intent is preserved. For waiting workflows, this value is applied when the
|
|
504
|
+
* workflow transitions from 'waiting' to 'scheduled'.
|
|
505
|
+
*/
|
|
506
|
+
delaySeconds?: number | null;
|
|
507
|
+
/**
|
|
508
|
+
* Absolute epoch-millisecond timestamp indicating the earliest time the queue message
|
|
509
|
+
* will be delivered. Computed as `enqueueTime + delaySeconds * 1000` at the moment the
|
|
510
|
+
* message is actually sent to the queue.
|
|
511
|
+
*
|
|
512
|
+
* - For directly-scheduled workflows: set at enqueue time.
|
|
513
|
+
* - For waiting workflows: set when dependencies finish and the workflow transitions to 'scheduled'.
|
|
514
|
+
* - `null` or absent when no delay was requested.
|
|
515
|
+
*/
|
|
516
|
+
scheduledFor?: number | null;
|
|
487
517
|
/** Workflows that this workflow depends on (must complete before this workflow can start) */
|
|
488
518
|
dependencies?: WorkflowDependencyRelation[];
|
|
489
519
|
/** Workflows that depend on this workflow (will start after this workflow completes) */
|
|
@@ -694,6 +724,12 @@ type WorkflowEnqueueBatchItem<I, O, TYPE extends string = string> = {
|
|
|
694
724
|
dependencies?: WorkflowDependency[];
|
|
695
725
|
/** Optional trigger identifier for workflow correlation. If not provided, one will be auto-generated. */
|
|
696
726
|
triggerId?: string | null;
|
|
727
|
+
/**
|
|
728
|
+
* Optional delay in seconds before the queue message is delivered.
|
|
729
|
+
* Maps directly to the Cloudflare Queue `delaySeconds` option.
|
|
730
|
+
* The message will not be delivered to a consumer until at least this many seconds have passed.
|
|
731
|
+
*/
|
|
732
|
+
delaySeconds?: number;
|
|
697
733
|
};
|
|
698
734
|
type WorkflowQueueMessage = {
|
|
699
735
|
type: 'workflow-run';
|
|
@@ -704,6 +740,8 @@ type WorkflowQueueMessage = {
|
|
|
704
740
|
triggerId?: string | null;
|
|
705
741
|
queueIdentifier?: string;
|
|
706
742
|
scheduledInstanceId?: string | undefined;
|
|
743
|
+
/** Queue delivery delay in seconds (passed through to Cloudflare Queue) */
|
|
744
|
+
delaySeconds?: number;
|
|
707
745
|
} | {
|
|
708
746
|
type: 'workflow-retry';
|
|
709
747
|
workflowType: string;
|
|
@@ -713,6 +751,7 @@ type WorkflowQueueMessage = {
|
|
|
713
751
|
/** If true the workflow will attempt to reuse all results from successful steps. Defaults to True */
|
|
714
752
|
reuseSuccessfulSteps?: boolean;
|
|
715
753
|
queueIdentifier?: string;
|
|
754
|
+
scheduledInstanceId?: string | undefined;
|
|
716
755
|
};
|
|
717
756
|
type RetryWorkflowOptions = {
|
|
718
757
|
/** If true the retry will attept to reuse all results from successful steps. Defaults to True */
|
|
@@ -723,6 +762,8 @@ type RetryWorkflowParams<I, O> = {
|
|
|
723
762
|
retryInstanceId: string;
|
|
724
763
|
triggerId?: string | null;
|
|
725
764
|
retryOptions?: RetryWorkflowOptions;
|
|
765
|
+
/** Optional: Provide an existing workflow instance ID. When provided, retry will update the existing instance instead of creating a new one. */
|
|
766
|
+
scheduledInstanceId?: string | undefined;
|
|
726
767
|
};
|
|
727
768
|
type WorkflowCallParams<I, O> = {
|
|
728
769
|
workflow: WorkflowFunction<I, O>;
|
|
@@ -878,7 +919,7 @@ declare const createLogAccessor: (context: {
|
|
|
878
919
|
declare function createQueueWorkflowContext(options: QueueWorkflowContextOptions): {
|
|
879
920
|
enqueueWorkflow: <I, O, TYPE extends string>(params: WorkflowEnqueueBatchItem<I, O, TYPE>) => Promise<ScheduledWorkflowExecutionStub<O, TYPE>>;
|
|
880
921
|
enqueueWorkflowBatch: <I, O>(workflows: Array<WorkflowEnqueueBatchItem<I, O>>) => Promise<Array<ScheduledWorkflowExecutionStub<O, string>>>;
|
|
881
|
-
enqueueRetryWorkflow: <I, O>(workflow: WorkflowFunction<I, O>, tenantId: string, oldInstanceId: string, reuseSuccessfulSteps?: boolean) => Promise<
|
|
922
|
+
enqueueRetryWorkflow: <I, O, TYPE extends string>(workflow: WorkflowFunction<I, O, TYPE>, tenantId: string, oldInstanceId: string, reuseSuccessfulSteps?: boolean, delaySeconds?: number) => Promise<ScheduledWorkflowExecutionStub<O, TYPE>>;
|
|
882
923
|
handleWorkflowQueueMessage: ({
|
|
883
924
|
message,
|
|
884
925
|
env,
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,10 @@ async function detectSchemaVersion(db, retryConfig) {
|
|
|
9
9
|
const hasInputRef = workflowTableInfo.sql.includes("inputRef");
|
|
10
10
|
const hasTriggerId = workflowTableInfo.sql.includes("triggerId");
|
|
11
11
|
const hasResultRef = workflowTableInfo.sql.includes("resultRef");
|
|
12
|
+
const hasDelaySeconds = workflowTableInfo.sql.includes("delaySeconds");
|
|
12
13
|
const inputHasNotNull = workflowTableInfo.sql.includes("input TEXT NOT NULL");
|
|
13
|
-
if (hasResultRef && hasTriggerId && hasInputRef && !inputHasNotNull) workflowTable = "
|
|
14
|
+
if (hasDelaySeconds && hasResultRef && hasTriggerId && hasInputRef && !inputHasNotNull) workflowTable = "v8";
|
|
15
|
+
else if (hasResultRef && hasTriggerId && hasInputRef && !inputHasNotNull) workflowTable = "v6";
|
|
14
16
|
else if (hasTriggerId && hasInputRef && !inputHasNotNull && !hasResultRef) workflowTable = "v4";
|
|
15
17
|
else if (hasInputRef && !inputHasNotNull && !hasTriggerId) workflowTable = "v2";
|
|
16
18
|
else if (!hasInputRef && inputHasNotNull) workflowTable = "v1";
|
|
@@ -123,6 +125,23 @@ async function migrateWorkflowTableV4ToV6(db, retryConfig) {
|
|
|
123
125
|
if (!hasResultRef) await retryD1Operation(() => db.batch([db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN result TEXT`), db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN resultRef TEXT`)]), retryConfig);
|
|
124
126
|
}
|
|
125
127
|
/**
|
|
128
|
+
* Migrate WorkflowTable from V6 to V8 schema
|
|
129
|
+
* Adds delaySeconds and scheduledFor columns for delayed workflow scheduling.
|
|
130
|
+
*
|
|
131
|
+
* - `delaySeconds` (INTEGER, nullable): The original delay intent in seconds. Preserved so that
|
|
132
|
+
* waiting workflows can apply the delay when they transition to 'scheduled'.
|
|
133
|
+
* - `scheduledFor` (INTEGER, nullable): Absolute epoch-millisecond timestamp indicating the
|
|
134
|
+
* earliest time the queue message will be delivered. For directly-scheduled workflows this is
|
|
135
|
+
* computed at enqueue time (`startTime + delaySeconds * 1000`). For waiting workflows it is
|
|
136
|
+
* computed at transition time (`transitionTime + delaySeconds * 1000`). NULL when no delay
|
|
137
|
+
* was requested.
|
|
138
|
+
*/
|
|
139
|
+
async function migrateWorkflowTableV6ToV8(db, retryConfig) {
|
|
140
|
+
const workflowTableInfo = await retryD1Operation(() => db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='WorkflowTable'`).first(), retryConfig);
|
|
141
|
+
const hasDelaySeconds = workflowTableInfo.sql.includes("delaySeconds");
|
|
142
|
+
if (!hasDelaySeconds) await retryD1Operation(() => db.batch([db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN delaySeconds INTEGER`), db.prepare(`ALTER TABLE WorkflowTable ADD COLUMN scheduledFor INTEGER`)]), retryConfig);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
126
145
|
* Create or migrate WorkflowTable to the latest schema
|
|
127
146
|
*/
|
|
128
147
|
async function migrateWorkflowTable(db, currentVersion, retryConfig) {
|
|
@@ -142,6 +161,8 @@ async function migrateWorkflowTable(db, currentVersion, retryConfig) {
|
|
|
142
161
|
endTime INTEGER,
|
|
143
162
|
parentInstanceId TEXT,
|
|
144
163
|
triggerId TEXT,
|
|
164
|
+
delaySeconds INTEGER,
|
|
165
|
+
scheduledFor INTEGER,
|
|
145
166
|
PRIMARY KEY (instanceId),
|
|
146
167
|
UNIQUE (triggerId)
|
|
147
168
|
)`).run(), retryConfig);
|
|
@@ -151,15 +172,22 @@ async function migrateWorkflowTable(db, currentVersion, retryConfig) {
|
|
|
151
172
|
await migrateWorkflowTableV1ToV2(db, retryConfig);
|
|
152
173
|
await migrateWorkflowTableV2V3ToV4(db, retryConfig);
|
|
153
174
|
await migrateWorkflowTableV4ToV6(db, retryConfig);
|
|
175
|
+
await migrateWorkflowTableV6ToV8(db, retryConfig);
|
|
154
176
|
return;
|
|
155
177
|
}
|
|
156
178
|
if (currentVersion === "v2") {
|
|
157
179
|
await migrateWorkflowTableV2V3ToV4(db, retryConfig);
|
|
158
180
|
await migrateWorkflowTableV4ToV6(db, retryConfig);
|
|
181
|
+
await migrateWorkflowTableV6ToV8(db, retryConfig);
|
|
159
182
|
return;
|
|
160
183
|
}
|
|
161
184
|
if (currentVersion === "v4") {
|
|
162
185
|
await migrateWorkflowTableV4ToV6(db, retryConfig);
|
|
186
|
+
await migrateWorkflowTableV6ToV8(db, retryConfig);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (currentVersion === "v6") {
|
|
190
|
+
await migrateWorkflowTableV6ToV8(db, retryConfig);
|
|
163
191
|
return;
|
|
164
192
|
}
|
|
165
193
|
}
|
|
@@ -468,11 +496,11 @@ async function finalizeWorkflowRecord(options, { workflowStatus, endTime, instan
|
|
|
468
496
|
*
|
|
469
497
|
* @internal
|
|
470
498
|
*/
|
|
471
|
-
async function prepareWorkflowInsertStatements(options, { instanceId, workflowType, workflowName, workflowMetadata, input, workflowStatus, startTime, endTime, parentInstanceId, tenantId, triggerId, dependencies }) {
|
|
499
|
+
async function prepareWorkflowInsertStatements(options, { instanceId, workflowType, workflowName, workflowMetadata, input, workflowStatus, startTime, endTime, parentInstanceId, tenantId, triggerId, dependencies, delaySeconds, scheduledFor }) {
|
|
472
500
|
const { data: inputData, externalRef: inputRef } = await serializeWithExternalStorage(input, options.serializer, options.externalBlobStorage);
|
|
473
501
|
const insertWorkflowStatement = options.D1.prepare(`INSERT INTO WorkflowTable
|
|
474
|
-
(instanceId, workflowType, workflowName, workflowMetadata, input, inputRef, tenantId, workflowStatus, startTime, endTime, parentInstanceId, triggerId)
|
|
475
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).bind(instanceId, workflowType, workflowName, options.serializer.serialize(workflowMetadata), inputData, inputRef, tenantId, workflowStatus, startTime, endTime ?? null, parentInstanceId ?? null, triggerId ?? null);
|
|
502
|
+
(instanceId, workflowType, workflowName, workflowMetadata, input, inputRef, tenantId, workflowStatus, startTime, endTime, parentInstanceId, triggerId, delaySeconds, scheduledFor)
|
|
503
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).bind(instanceId, workflowType, workflowName, options.serializer.serialize(workflowMetadata), inputData, inputRef, tenantId, workflowStatus, startTime, endTime ?? null, parentInstanceId ?? null, triggerId ?? null, delaySeconds ?? null, scheduledFor ?? null);
|
|
476
504
|
if (!dependencies || dependencies.length === 0) return {
|
|
477
505
|
statements: [insertWorkflowStatement],
|
|
478
506
|
instanceId,
|
|
@@ -777,7 +805,9 @@ async function workflowTableRowToWorkflowRun({ row, serializer, externalBlobStor
|
|
|
777
805
|
startTime: row.startTime,
|
|
778
806
|
endTime: row.endTime,
|
|
779
807
|
parentInstanceId: row.parentInstanceId,
|
|
780
|
-
triggerId: row.triggerId
|
|
808
|
+
triggerId: row.triggerId,
|
|
809
|
+
delaySeconds: row.delaySeconds ?? null,
|
|
810
|
+
scheduledFor: row.scheduledFor ?? null
|
|
781
811
|
};
|
|
782
812
|
}
|
|
783
813
|
async function updateWorkflowName(context, instanceId, newWorkflowName) {
|
|
@@ -840,6 +870,14 @@ async function updateWorkflow(context, instanceId, updates) {
|
|
|
840
870
|
setClauses.push("triggerId = ?");
|
|
841
871
|
bindings.push(updates.triggerId);
|
|
842
872
|
}
|
|
873
|
+
if (updates.delaySeconds !== void 0) {
|
|
874
|
+
setClauses.push("delaySeconds = ?");
|
|
875
|
+
bindings.push(updates.delaySeconds);
|
|
876
|
+
}
|
|
877
|
+
if (updates.scheduledFor !== void 0) {
|
|
878
|
+
setClauses.push("scheduledFor = ?");
|
|
879
|
+
bindings.push(updates.scheduledFor);
|
|
880
|
+
}
|
|
843
881
|
if (updates.workflowMetadata !== void 0) {
|
|
844
882
|
setClauses.push("workflowMetadata = ?");
|
|
845
883
|
bindings.push(context.serializer.serialize(updates.workflowMetadata));
|
|
@@ -1785,7 +1823,7 @@ function createQueueWorkflowContext(options) {
|
|
|
1785
1823
|
};
|
|
1786
1824
|
const idFactory = internalContext.idFactory;
|
|
1787
1825
|
const enqueueWorkflow = async (params) => {
|
|
1788
|
-
const { workflow, tenantId, input, initialName, dependencies, triggerId: providedTriggerId } = params;
|
|
1826
|
+
const { workflow, tenantId, input, initialName, dependencies, triggerId: providedTriggerId, delaySeconds } = params;
|
|
1789
1827
|
const triggerId = providedTriggerId ?? idFactory();
|
|
1790
1828
|
const instanceId = idFactory();
|
|
1791
1829
|
const startTime = Date.now();
|
|
@@ -1801,9 +1839,12 @@ function createQueueWorkflowContext(options) {
|
|
|
1801
1839
|
parentInstanceId: void 0,
|
|
1802
1840
|
tenantId,
|
|
1803
1841
|
triggerId,
|
|
1804
|
-
dependencies
|
|
1842
|
+
dependencies,
|
|
1843
|
+
delaySeconds: delaySeconds ?? null,
|
|
1844
|
+
scheduledFor: null
|
|
1805
1845
|
});
|
|
1806
1846
|
else {
|
|
1847
|
+
const scheduledFor = delaySeconds != null ? startTime + delaySeconds * 1e3 : null;
|
|
1807
1848
|
await insertWorkflowRecord(internalContext, {
|
|
1808
1849
|
instanceId,
|
|
1809
1850
|
workflowType: workflow.workflowType,
|
|
@@ -1815,7 +1856,9 @@ function createQueueWorkflowContext(options) {
|
|
|
1815
1856
|
endTime: null,
|
|
1816
1857
|
parentInstanceId: void 0,
|
|
1817
1858
|
tenantId,
|
|
1818
|
-
triggerId
|
|
1859
|
+
triggerId,
|
|
1860
|
+
delaySeconds: delaySeconds ?? null,
|
|
1861
|
+
scheduledFor
|
|
1819
1862
|
});
|
|
1820
1863
|
await options.QUEUE.send({
|
|
1821
1864
|
type: "workflow-run",
|
|
@@ -1826,15 +1869,42 @@ function createQueueWorkflowContext(options) {
|
|
|
1826
1869
|
triggerId,
|
|
1827
1870
|
queueIdentifier: options.queueIdentifier,
|
|
1828
1871
|
scheduledInstanceId: instanceId
|
|
1829
|
-
});
|
|
1872
|
+
}, delaySeconds != null ? { delaySeconds } : void 0);
|
|
1830
1873
|
}
|
|
1831
1874
|
return {
|
|
1832
1875
|
instanceId,
|
|
1833
1876
|
workflowType: workflow.workflowType
|
|
1834
1877
|
};
|
|
1835
1878
|
};
|
|
1836
|
-
const enqueueRetryWorkflow = async (workflow, tenantId, oldInstanceId, reuseSuccessfulSteps) => {
|
|
1879
|
+
const enqueueRetryWorkflow = async (workflow, tenantId, oldInstanceId, reuseSuccessfulSteps, delaySeconds) => {
|
|
1837
1880
|
const triggerId = idFactory();
|
|
1881
|
+
const instanceId = idFactory();
|
|
1882
|
+
const startTime = Date.now();
|
|
1883
|
+
const scheduledFor = delaySeconds != null ? startTime + delaySeconds * 1e3 : null;
|
|
1884
|
+
const logAccessor = createLogAccessor({
|
|
1885
|
+
D1: options.D1,
|
|
1886
|
+
externalBlobStorage: options.externalBlobStorage,
|
|
1887
|
+
serializer: internalContext.serializer,
|
|
1888
|
+
tenantId,
|
|
1889
|
+
retryConfig: options.retryConfig
|
|
1890
|
+
});
|
|
1891
|
+
const oldRun = await logAccessor.getWorkflowShallow(oldInstanceId, { populateInput: true });
|
|
1892
|
+
if (!oldRun) throw new Error(`Cannot retry: no workflow found for instanceId ${oldInstanceId}`);
|
|
1893
|
+
await insertWorkflowRecord(internalContext, {
|
|
1894
|
+
instanceId,
|
|
1895
|
+
workflowType: workflow.workflowType,
|
|
1896
|
+
workflowName: oldRun.workflowName ?? "unknown",
|
|
1897
|
+
workflowMetadata: workflow.metadata,
|
|
1898
|
+
input: oldRun.input,
|
|
1899
|
+
workflowStatus: "scheduled",
|
|
1900
|
+
startTime,
|
|
1901
|
+
endTime: null,
|
|
1902
|
+
parentInstanceId: oldInstanceId,
|
|
1903
|
+
tenantId,
|
|
1904
|
+
triggerId,
|
|
1905
|
+
delaySeconds: delaySeconds ?? null,
|
|
1906
|
+
scheduledFor
|
|
1907
|
+
});
|
|
1838
1908
|
await options.QUEUE.send({
|
|
1839
1909
|
type: "workflow-retry",
|
|
1840
1910
|
workflowType: workflow.workflowType,
|
|
@@ -1842,8 +1912,13 @@ function createQueueWorkflowContext(options) {
|
|
|
1842
1912
|
tenantId,
|
|
1843
1913
|
triggerId,
|
|
1844
1914
|
reuseSuccessfulSteps: reuseSuccessfulSteps ?? true,
|
|
1845
|
-
queueIdentifier: options.queueIdentifier
|
|
1846
|
-
|
|
1915
|
+
queueIdentifier: options.queueIdentifier,
|
|
1916
|
+
scheduledInstanceId: instanceId
|
|
1917
|
+
}, delaySeconds != null ? { delaySeconds } : void 0);
|
|
1918
|
+
return {
|
|
1919
|
+
instanceId,
|
|
1920
|
+
workflowType: workflow.workflowType
|
|
1921
|
+
};
|
|
1847
1922
|
};
|
|
1848
1923
|
/**
|
|
1849
1924
|
* Enqueue multiple workflows in a single batch operation.
|
|
@@ -1866,10 +1941,11 @@ function createQueueWorkflowContext(options) {
|
|
|
1866
1941
|
if (workflows.length > 100) throw new Error(`enqueueWorkflowBatch: Cannot enqueue more than 100 workflows in a single batch (received ${workflows.length}). Split into smaller chunks.`);
|
|
1867
1942
|
const startTime = Date.now();
|
|
1868
1943
|
console.log(`enqueueWorkflowBatch: Starting batch of ${workflows.length} workflows`);
|
|
1869
|
-
const preparedWorkflows = await Promise.all(workflows.map(async ({ workflow, tenantId, input, initialName, dependencies, triggerId: providedTriggerId }) => {
|
|
1944
|
+
const preparedWorkflows = await Promise.all(workflows.map(async ({ workflow, tenantId, input, initialName, dependencies, triggerId: providedTriggerId, delaySeconds }) => {
|
|
1870
1945
|
const instanceId = idFactory();
|
|
1871
1946
|
const triggerId = providedTriggerId ?? idFactory();
|
|
1872
1947
|
const hasDependencies = dependencies && dependencies.length > 0;
|
|
1948
|
+
const scheduledFor = !hasDependencies && delaySeconds != null ? startTime + delaySeconds * 1e3 : null;
|
|
1873
1949
|
const prepared = await prepareWorkflowInsertStatements(internalContext, {
|
|
1874
1950
|
instanceId,
|
|
1875
1951
|
workflowType: workflow.workflowType,
|
|
@@ -1882,14 +1958,17 @@ function createQueueWorkflowContext(options) {
|
|
|
1882
1958
|
parentInstanceId: void 0,
|
|
1883
1959
|
tenantId,
|
|
1884
1960
|
triggerId,
|
|
1885
|
-
dependencies
|
|
1961
|
+
dependencies,
|
|
1962
|
+
delaySeconds: delaySeconds ?? null,
|
|
1963
|
+
scheduledFor
|
|
1886
1964
|
});
|
|
1887
1965
|
return {
|
|
1888
1966
|
...prepared,
|
|
1889
1967
|
tenantId,
|
|
1890
1968
|
triggerId,
|
|
1891
1969
|
initialName,
|
|
1892
|
-
shouldQueue: !hasDependencies
|
|
1970
|
+
shouldQueue: !hasDependencies,
|
|
1971
|
+
delaySeconds
|
|
1893
1972
|
};
|
|
1894
1973
|
}));
|
|
1895
1974
|
const workflowsWithDeps = preparedWorkflows.filter((p) => !p.shouldQueue).length;
|
|
@@ -1900,16 +1979,19 @@ function createQueueWorkflowContext(options) {
|
|
|
1900
1979
|
console.log(`enqueueWorkflowBatch: Executing D1 batch with ${allStatements.length} statements`);
|
|
1901
1980
|
await retryD1Operation(() => options.D1.batch(allStatements), options.retryConfig);
|
|
1902
1981
|
}
|
|
1903
|
-
const messagesToQueue = preparedWorkflows.filter((p) => p.shouldQueue).map((p) => ({
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1982
|
+
const messagesToQueue = preparedWorkflows.filter((p) => p.shouldQueue).map((p) => ({
|
|
1983
|
+
body: {
|
|
1984
|
+
type: "workflow-run",
|
|
1985
|
+
workflowType: p.workflowType,
|
|
1986
|
+
workflowName: p.initialName,
|
|
1987
|
+
input: workflows.find((w) => w.workflow.workflowType === p.workflowType && w.initialName === p.initialName)?.input,
|
|
1988
|
+
tenantId: p.tenantId,
|
|
1989
|
+
triggerId: p.triggerId,
|
|
1990
|
+
queueIdentifier: options.queueIdentifier,
|
|
1991
|
+
scheduledInstanceId: p.instanceId
|
|
1992
|
+
},
|
|
1993
|
+
...p.delaySeconds != null ? { delaySeconds: p.delaySeconds } : {}
|
|
1994
|
+
}));
|
|
1913
1995
|
if (messagesToQueue.length > 0) {
|
|
1914
1996
|
console.log(`enqueueWorkflowBatch: Sending ${messagesToQueue.length} messages to queue`);
|
|
1915
1997
|
await options.QUEUE.sendBatch(messagesToQueue);
|
|
@@ -1942,7 +2024,8 @@ function createQueueWorkflowContext(options) {
|
|
|
1942
2024
|
workflow: workflowFunction,
|
|
1943
2025
|
retryInstanceId: message.retryInstanceId,
|
|
1944
2026
|
triggerId: message.triggerId,
|
|
1945
|
-
retryOptions: { reuseSuccessfulSteps: message.reuseSuccessfulSteps ?? true }
|
|
2027
|
+
retryOptions: { reuseSuccessfulSteps: message.reuseSuccessfulSteps ?? true },
|
|
2028
|
+
scheduledInstanceId: message.scheduledInstanceId
|
|
1946
2029
|
});
|
|
1947
2030
|
}
|
|
1948
2031
|
};
|
|
@@ -1968,7 +2051,12 @@ function createQueueWorkflowContext(options) {
|
|
|
1968
2051
|
const allDepsFinished = dependencies.every((dep) => dep.workflow.workflowStatus === "completed" || dep.workflow.workflowStatus === "failed");
|
|
1969
2052
|
if (allDepsFinished) {
|
|
1970
2053
|
console.log(`Enqueuing waiting workflow ${wf.instanceId} as all dependencies are finished`);
|
|
1971
|
-
|
|
2054
|
+
const now = Date.now();
|
|
2055
|
+
const scheduledFor = wf.delaySeconds != null ? now + wf.delaySeconds * 1e3 : null;
|
|
2056
|
+
await updateWorkflow(internalContext, wf.instanceId, {
|
|
2057
|
+
workflowStatus: "scheduled",
|
|
2058
|
+
scheduledFor
|
|
2059
|
+
});
|
|
1972
2060
|
await options.QUEUE.send({
|
|
1973
2061
|
type: "workflow-run",
|
|
1974
2062
|
workflowType: wf.workflowType,
|
|
@@ -1978,7 +2066,7 @@ function createQueueWorkflowContext(options) {
|
|
|
1978
2066
|
triggerId: wf.triggerId,
|
|
1979
2067
|
queueIdentifier: options.queueIdentifier,
|
|
1980
2068
|
scheduledInstanceId: wf.instanceId
|
|
1981
|
-
});
|
|
2069
|
+
}, wf.delaySeconds != null ? { delaySeconds: wf.delaySeconds } : void 0);
|
|
1982
2070
|
} else {
|
|
1983
2071
|
const unfinishedDeps = dependencies.filter((dep) => dep.workflow.workflowStatus !== "completed" && dep.workflow.workflowStatus !== "failed");
|
|
1984
2072
|
console.log(`Workflow ${wf.instanceId} is still waiting on dependencies. Unfinished dependencies: ${unfinishedDeps.map((d) => d.workflow.instanceId + ": " + d.workflow.workflowStatus).join(", ")}`);
|
|
@@ -2244,7 +2332,7 @@ function createWorkflowContext(options) {
|
|
|
2244
2332
|
throw error;
|
|
2245
2333
|
}
|
|
2246
2334
|
};
|
|
2247
|
-
const retry = async ({ workflow, retryInstanceId, triggerId, retryOptions }) => {
|
|
2335
|
+
const retry = async ({ workflow, retryInstanceId, triggerId, retryOptions, scheduledInstanceId }) => {
|
|
2248
2336
|
if (!ensuredTables) {
|
|
2249
2337
|
await ensureTables(options.D1, options.retryConfig);
|
|
2250
2338
|
ensuredTables = true;
|
|
@@ -2264,7 +2352,8 @@ function createWorkflowContext(options) {
|
|
|
2264
2352
|
parentInstanceId: retryInstanceId,
|
|
2265
2353
|
reuseSuccessfulSteps: retryOptions?.reuseSuccessfulSteps,
|
|
2266
2354
|
tenantId,
|
|
2267
|
-
triggerId
|
|
2355
|
+
triggerId,
|
|
2356
|
+
scheduledInstanceId
|
|
2268
2357
|
});
|
|
2269
2358
|
};
|
|
2270
2359
|
return {
|