Skip to content

Commit 57f45f1

Browse files
committed
support debounce options when batch triggering
1 parent 96c55cc commit 57f45f1

File tree

8 files changed

+189
-6
lines changed

8 files changed

+189
-6
lines changed

.changeset/ninety-cows-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/sdk": patch
3+
---
4+
5+
feat(sdk): Support debouncing runs when triggering with new debounce options

packages/cli-v3/src/build/manifests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export async function copyManifestToDir(
5454
*/
5555
async function computeFileHash(filePath: string): Promise<string> {
5656
const contents = await readFile(filePath);
57-
return createHash("sha256").update(contents).digest("hex").slice(0, 16);
57+
return createHash("sha256").update(contents as Uint8Array).digest("hex").slice(0, 16);
5858
}
5959

6060
/**

packages/cli-v3/src/entryPoints/dev-run-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ export class DevRunController {
428428
}
429429
case "RUN_CREATED":
430430
case "QUEUED_EXECUTING":
431-
case "QUEUED": {
431+
case "QUEUED":
432+
case "DELAYED": {
432433
logger.debug("Status change not handled", { status: snapshot.executionStatus });
433434
return;
434435
}

packages/cli-v3/src/entryPoints/managed/execution.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,10 @@ export class RunExecution {
372372

373373
return;
374374
}
375-
case "RUN_CREATED": {
375+
case "RUN_CREATED":
376+
case "DELAYED": {
376377
this.sendDebugLog(
377-
"aborting execution: invalid status change: RUN_CREATED",
378+
"aborting execution: invalid status change: RUN_CREATED or DELAYED",
378379
snapshotMetadata
379380
);
380381

packages/core/src/v3/schemas/api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ export const BatchTriggerTaskItem = z.object({
257257
ttl: z.string().or(z.number().nonnegative().int()).optional(),
258258
priority: z.number().optional(),
259259
region: z.string().optional(),
260+
debounce: z
261+
.object({
262+
key: z.string(),
263+
delay: z.string(),
264+
})
265+
.optional(),
260266
})
261267
.optional(),
262268
});

packages/core/src/v3/zodSocket.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export class ZodSocketMessageHandler<TRPCCatalog extends ZodSocketMessageCatalog
100100
this.#handlers = options.handlers;
101101
this.#logger =
102102
options.logger ?? new SimpleStructuredLogger("socket-message-handler", LogLevel.info);
103-
this.#logPayloads = options.logPayloads ?? !!process.env.LOG_SOCKET_HANDLER_PAYLOADS ?? false;
103+
this.#logPayloads = options.logPayloads ?? !!process.env.LOG_SOCKET_HANDLER_PAYLOADS;
104104
}
105105

106106
public async handleMessage(message: unknown) {

packages/trigger-sdk/src/v3/shared.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ export async function batchTriggerById<TTask extends AnyTask>(
649649
priority: item.options?.priority,
650650
region: item.options?.region,
651651
lockToVersion: item.options?.version ?? getEnvVar("TRIGGER_VERSION"),
652+
debounce: item.options?.debounce,
652653
},
653654
};
654655
})
@@ -904,6 +905,7 @@ export async function batchTriggerByIdAndWait<TTask extends AnyTask>(
904905
machine: item.options?.machine,
905906
priority: item.options?.priority,
906907
region: item.options?.region,
908+
debounce: item.options?.debounce,
907909
},
908910
};
909911
})
@@ -1163,6 +1165,7 @@ export async function batchTriggerTasks<TTasks extends readonly AnyTask[]>(
11631165
priority: item.options?.priority,
11641166
region: item.options?.region,
11651167
lockToVersion: item.options?.version ?? getEnvVar("TRIGGER_VERSION"),
1168+
debounce: item.options?.debounce,
11661169
},
11671170
};
11681171
})
@@ -1423,6 +1426,7 @@ export async function batchTriggerAndWaitTasks<TTasks extends readonly AnyTask[]
14231426
machine: item.options?.machine,
14241427
priority: item.options?.priority,
14251428
region: item.options?.region,
1429+
debounce: item.options?.debounce,
14261430
},
14271431
};
14281432
})
@@ -1758,6 +1762,7 @@ async function* transformBatchItemsStream<TTask extends AnyTask>(
17581762
priority: item.options?.priority,
17591763
region: item.options?.region,
17601764
lockToVersion: item.options?.version ?? getEnvVar("TRIGGER_VERSION"),
1765+
debounce: item.options?.debounce,
17611766
},
17621767
};
17631768
}
@@ -1809,6 +1814,7 @@ async function* transformBatchItemsStreamForWait<TTask extends AnyTask>(
18091814
machine: item.options?.machine,
18101815
priority: item.options?.priority,
18111816
region: item.options?.region,
1817+
debounce: item.options?.debounce,
18121818
},
18131819
};
18141820
}
@@ -1859,6 +1865,7 @@ async function* transformBatchByTaskItemsStream<TTasks extends readonly AnyTask[
18591865
priority: item.options?.priority,
18601866
region: item.options?.region,
18611867
lockToVersion: item.options?.version ?? getEnvVar("TRIGGER_VERSION"),
1868+
debounce: item.options?.debounce,
18621869
},
18631870
};
18641871
}
@@ -1909,6 +1916,7 @@ async function* transformBatchByTaskItemsStreamForWait<TTasks extends readonly A
19091916
machine: item.options?.machine,
19101917
priority: item.options?.priority,
19111918
region: item.options?.region,
1919+
debounce: item.options?.debounce,
19121920
},
19131921
};
19141922
}
@@ -1961,6 +1969,7 @@ async function* transformSingleTaskBatchItemsStream<TPayload>(
19611969
priority: item.options?.priority,
19621970
region: item.options?.region,
19631971
lockToVersion: item.options?.version ?? getEnvVar("TRIGGER_VERSION"),
1972+
debounce: item.options?.debounce,
19641973
},
19651974
};
19661975
}
@@ -2013,6 +2022,7 @@ async function* transformSingleTaskBatchItemsStreamForWait<TPayload>(
20132022
machine: item.options?.machine,
20142023
priority: item.options?.priority,
20152024
region: item.options?.region,
2025+
debounce: item.options?.debounce,
20162026
},
20172027
};
20182028
}

references/hello-world/src/trigger/debounce.ts

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { logger, task, wait } from "@trigger.dev/sdk/v3";
1+
import { batch, logger, task, wait } from "@trigger.dev/sdk/v3";
22

33
/**
44
* A simple task that processes data updates.
@@ -570,3 +570,163 @@ export const testDifferentDelays = task({
570570
return { triggered: true };
571571
},
572572
});
573+
574+
/**
575+
* Example 7: Batch Trigger with Debounce
576+
*
577+
* Demonstrates using debounce with batchTrigger.
578+
* Each item in the batch can have its own debounce key and delay.
579+
* Items with the same debounce key will be consolidated into a single run.
580+
*/
581+
export const batchItemTask = task({
582+
id: "batch-item-task",
583+
run: async (payload: { itemId: string; data: string }) => {
584+
logger.info("Processing batch item", { payload });
585+
586+
await wait.for({ seconds: 1 });
587+
588+
logger.info("Batch item processed", { itemId: payload.itemId });
589+
590+
return {
591+
processed: true,
592+
itemId: payload.itemId,
593+
processedAt: new Date().toISOString(),
594+
};
595+
},
596+
});
597+
598+
/**
599+
* Demonstrates batch.trigger() with debounce options on individual items.
600+
*
601+
* This shows how you can:
602+
* - Use different debounce keys for different items
603+
* - Items with the same debounce key will be consolidated
604+
* - Items with different keys will create separate runs
605+
*
606+
* Run this task and watch:
607+
* - Items 1 and 3 share debounce key "group-a" -> ONE run
608+
* - Items 2 and 4 share debounce key "group-b" -> ONE run
609+
* - Item 5 has unique key "group-c" -> ONE run
610+
* - Total: 3 runs instead of 5 (but batch shows 5 items)
611+
*
612+
* Note: The batch itself still reports 5 items, but only 3 actual task runs
613+
* will execute due to debouncing.
614+
*/
615+
export const demonstrateBatchDebounce = task({
616+
id: "demonstrate-batch-debounce",
617+
run: async (payload: { prefix?: string }) => {
618+
const prefix = payload.prefix ?? "batch-demo";
619+
620+
logger.info("Starting batch debounce demonstration");
621+
logger.info("Will trigger 5 items with 3 different debounce keys");
622+
logger.info(
623+
"Items 1&3 share key 'group-a', items 2&4 share key 'group-b', item 5 has key 'group-c'"
624+
);
625+
626+
// Use batch.trigger with debounce options on each item
627+
const result = await batch.trigger<typeof batchItemTask>([
628+
{
629+
id: "batch-item-task",
630+
payload: { itemId: `${prefix}-1`, data: "First item in group A" },
631+
options: {
632+
debounce: { key: `${prefix}-group-a`, delay: "5s" },
633+
},
634+
},
635+
{
636+
id: "batch-item-task",
637+
payload: { itemId: `${prefix}-2`, data: "First item in group B" },
638+
options: {
639+
debounce: { key: `${prefix}-group-b`, delay: "5s" },
640+
},
641+
},
642+
{
643+
id: "batch-item-task",
644+
payload: { itemId: `${prefix}-3`, data: "Second item in group A (debounced)" },
645+
options: {
646+
debounce: { key: `${prefix}-group-a`, delay: "5s" },
647+
},
648+
},
649+
{
650+
id: "batch-item-task",
651+
payload: { itemId: `${prefix}-4`, data: "Second item in group B (debounced)" },
652+
options: {
653+
debounce: { key: `${prefix}-group-b`, delay: "5s" },
654+
},
655+
},
656+
{
657+
id: "batch-item-task",
658+
payload: { itemId: `${prefix}-5`, data: "Only item in group C" },
659+
options: {
660+
debounce: { key: `${prefix}-group-c`, delay: "5s" },
661+
},
662+
},
663+
]);
664+
665+
logger.info("Batch debounce demonstration complete", {
666+
batchId: result.batchId,
667+
totalItemsInBatch: result.runCount,
668+
note: "Check the dashboard - only 3 actual task runs should execute due to debouncing",
669+
});
670+
671+
return {
672+
batchId: result.batchId,
673+
totalItemsInBatch: result.runCount,
674+
expectedUniqueRuns: 3,
675+
message:
676+
"5 items submitted, but only 3 runs will execute: group-a (1 run), group-b (1 run), group-c (1 run)",
677+
};
678+
},
679+
});
680+
681+
/**
682+
* Demonstrates batchTrigger on a single task with debounce.
683+
*
684+
* Similar to batch.trigger but using myTask.batchTrigger() syntax.
685+
* Each item can have its own debounce configuration.
686+
*
687+
* When all items share the same debounce key, only ONE run will execute.
688+
*/
689+
export const demonstrateSingleTaskBatchDebounce = task({
690+
id: "demonstrate-single-task-batch-debounce",
691+
run: async (payload: { debounceKey?: string }) => {
692+
const key = payload.debounceKey ?? "single-batch-demo";
693+
694+
logger.info("Starting single task batch debounce demonstration", { debounceKey: key });
695+
logger.info("Triggering 4 items with the SAME debounce key - only 1 run should execute");
696+
697+
// All items have the same debounce key, so they should all resolve to the same run
698+
const result = await batchItemTask.batchTrigger([
699+
{
700+
payload: { itemId: `${key}-1`, data: "Item 1" },
701+
options: { debounce: { key, delay: "5s" } },
702+
},
703+
{
704+
payload: { itemId: `${key}-2`, data: "Item 2" },
705+
options: { debounce: { key, delay: "5s" } },
706+
},
707+
{
708+
payload: { itemId: `${key}-3`, data: "Item 3" },
709+
options: { debounce: { key, delay: "5s" } },
710+
},
711+
{
712+
payload: { itemId: `${key}-4`, data: "Item 4" },
713+
options: { debounce: { key, delay: "5s" } },
714+
},
715+
]);
716+
717+
logger.info("Single task batch debounce complete", {
718+
batchId: result.batchId,
719+
totalItemsInBatch: result.runCount,
720+
debounceKey: key,
721+
note: "All items share the same debounce key, so only 1 task run should execute",
722+
});
723+
724+
return {
725+
batchId: result.batchId,
726+
totalItemsInBatch: result.runCount,
727+
debounceKey: key,
728+
expectedUniqueRuns: 1,
729+
message: "4 items submitted with same debounce key - only 1 run will execute",
730+
};
731+
},
732+
});

0 commit comments

Comments
 (0)