@@ -13,11 +13,15 @@ import { BatchProcessingStrategy } from "~/v3/services/batchTriggerV3.server";
1313import { OutOfEntitlementError } from "~/v3/services/triggerTask.server" ;
1414import { HeadersSchema } from "./api.v1.tasks.$taskId.trigger" ;
1515import { RunEngineBatchTriggerService } from "~/runEngine/services/batchTrigger.server" ;
16+ import { z } from "zod" ;
17+ import { requestIdempotency } from "~/services/requestIdempotencyInstance.server" ;
18+ import { prisma } from "~/db.server" ;
1619
1720const { action, loader } = createActionApiRoute (
1821 {
1922 headers : HeadersSchema . extend ( {
2023 "batch-processing-strategy" : BatchProcessingStrategy . nullish ( ) ,
24+ "x-trigger-request-id" : z . string ( ) . nullish ( ) ,
2125 } ) ,
2226 body : BatchTriggerTaskV3RequestBody ,
2327 allowJWT : true ,
@@ -53,6 +57,7 @@ const { action, loader } = createActionApiRoute(
5357 "x-trigger-client" : triggerClient ,
5458 "x-trigger-engine-version" : engineVersion ,
5559 "batch-processing-strategy" : batchProcessingStrategy ,
60+ "x-trigger-request-id" : requestId ,
5661 traceparent,
5762 tracestate,
5863 } = headers ;
@@ -67,15 +72,72 @@ const { action, loader } = createActionApiRoute(
6772 traceparent,
6873 tracestate,
6974 batchProcessingStrategy,
75+ requestId,
7076 } ) ;
7177
78+ if ( requestId ) {
79+ logger . debug ( "request-idempotency: checking for cached batch-trigger request" , {
80+ requestId,
81+ } ) ;
82+
83+ const cachedRequest = await requestIdempotency . checkRequest ( "batch-trigger" , requestId ) ;
84+
85+ if ( cachedRequest ) {
86+ logger . info ( "request-idempotency: found cached batch-trigger request" , {
87+ requestId,
88+ cachedRequest,
89+ } ) ;
90+
91+ // Find the batch and return it as a 200 response
92+ const cachedBatch = await prisma . batchTaskRun . findFirst ( {
93+ where : {
94+ id : cachedRequest . id ,
95+ runtimeEnvironmentId : authentication . environment . id ,
96+ } ,
97+ select : {
98+ friendlyId : true ,
99+ runCount : true ,
100+ } ,
101+ } ) ;
102+
103+ if ( cachedBatch ) {
104+ logger . info ( "request-idempotency: found cached batch-trigger request" , {
105+ requestId,
106+ cachedRequest,
107+ cachedBatch,
108+ } ) ;
109+
110+ const responseBody = {
111+ id : cachedBatch . friendlyId ,
112+ runCount : cachedBatch . runCount ,
113+ } ;
114+
115+ const $responseHeaders = await responseHeaders (
116+ responseBody ,
117+ authentication . environment ,
118+ triggerClient
119+ ) ;
120+
121+ return json ( responseBody , { status : 200 , headers : $responseHeaders } ) ;
122+ }
123+ }
124+ }
125+
72126 const traceContext =
73127 traceparent && isFromWorker // If the request is from a worker, we should pass the trace context
74128 ? { traceparent, tracestate }
75129 : undefined ;
76130
77131 const service = new RunEngineBatchTriggerService ( batchProcessingStrategy ?? undefined ) ;
78132
133+ service . onBatchTaskRunCreated . attachOnce ( async ( batch ) => {
134+ if ( requestId ) {
135+ await requestIdempotency . saveRequest ( "batch-trigger" , requestId , {
136+ id : batch . id ,
137+ } ) ;
138+ }
139+ } ) ;
140+
79141 try {
80142 const batch = await service . call ( authentication . environment , body , {
81143 triggerVersion : triggerVersion ?? undefined ,
@@ -90,7 +152,10 @@ const { action, loader } = createActionApiRoute(
90152 triggerClient
91153 ) ;
92154
93- return json ( batch , { status : 202 , headers : $responseHeaders } ) ;
155+ return json ( batch , {
156+ status : 202 ,
157+ headers : $responseHeaders ,
158+ } ) ;
94159 } catch ( error ) {
95160 logger . error ( "Batch trigger error" , {
96161 error : {
0 commit comments