Skip to content

Commit 60cdc34

Browse files
committed
refactor(idempotency): return the in-progress wait as a thunk instead of overloading on policy
1 parent 7541702 commit 60cdc34

2 files changed

Lines changed: 31 additions & 45 deletions

File tree

‎apps/sim/lib/core/idempotency/service.ts‎

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ export interface IdempotencyExecutionOptions {
8080
*/
8181
export type IdempotentExecution<T> = { outcome: 'resolved'; result: T } | { outcome: 'in-progress' }
8282

83-
type InProgressPolicy = 'wait' | 'skip'
83+
/** An in-progress outcome carries the wait on the live holder, so each caller decides whether to take it. */
84+
type ClaimedExecution<T> =
85+
| { outcome: 'resolved'; result: T }
86+
| { outcome: 'in-progress'; wait: () => Promise<T> }
8487

8588
export interface AtomicClaimResult {
8689
claimed: boolean
@@ -578,18 +581,17 @@ export class IdempotencyService {
578581
provider: string,
579582
identifier: string,
580583
operation: () => Promise<T>,
581-
additionalContext?: Record<string, any>,
584+
additionalContext?: Record<string, unknown>,
582585
options?: IdempotencyExecutionOptions
583586
): Promise<T> {
584587
const execution = await this.execute(
585588
provider,
586589
identifier,
587590
operation,
588591
additionalContext,
589-
options,
590-
'wait'
592+
options
591593
)
592-
return execution.result
594+
return execution.outcome === 'resolved' ? execution.result : execution.wait()
593595
}
594596

595597
/**
@@ -605,33 +607,25 @@ export class IdempotencyService {
605607
additionalContext?: Record<string, unknown>,
606608
options?: IdempotencyExecutionOptions
607609
): Promise<IdempotentExecution<T>> {
608-
return this.execute(provider, identifier, operation, additionalContext, options, 'skip')
610+
const execution = await this.execute(
611+
provider,
612+
identifier,
613+
operation,
614+
additionalContext,
615+
options
616+
)
617+
if (execution.outcome === 'resolved') return execution
618+
logger.info(`Skipping duplicate of in-progress operation: ${provider}:${identifier}`)
619+
return { outcome: 'in-progress' }
609620
}
610621

611-
private execute<T>(
612-
provider: string,
613-
identifier: string,
614-
operation: () => Promise<T>,
615-
additionalContext: Record<string, unknown> | undefined,
616-
options: IdempotencyExecutionOptions | undefined,
617-
inProgressPolicy: 'wait'
618-
): Promise<{ outcome: 'resolved'; result: T }>
619-
private execute<T>(
620-
provider: string,
621-
identifier: string,
622-
operation: () => Promise<T>,
623-
additionalContext: Record<string, unknown> | undefined,
624-
options: IdempotencyExecutionOptions | undefined,
625-
inProgressPolicy: InProgressPolicy
626-
): Promise<IdempotentExecution<T>>
627622
private async execute<T>(
628623
provider: string,
629624
identifier: string,
630625
operation: () => Promise<T>,
631626
additionalContext: Record<string, unknown> | undefined,
632-
options: IdempotencyExecutionOptions | undefined,
633-
inProgressPolicy: InProgressPolicy
634-
): Promise<IdempotentExecution<T>> {
627+
options: IdempotencyExecutionOptions | undefined
628+
): Promise<ClaimedExecution<T>> {
635629
const claimResult = await this.atomicallyClaim(provider, identifier, additionalContext, options)
636630

637631
if (!claimResult.claimed) {
@@ -651,32 +645,24 @@ export class IdempotencyService {
651645
observedResult: existingResult,
652646
observedValue: claimResult.observedValue,
653647
})
654-
return this.execute(
655-
provider,
656-
identifier,
657-
operation,
658-
additionalContext,
659-
options,
660-
inProgressPolicy
661-
)
648+
return this.execute(provider, identifier, operation, additionalContext, options)
662649
}
663650
logger.info(`Previous operation failed for: ${claimResult.normalizedKey}`)
664651
throw new Error(existingResult.error || 'Previous operation failed')
665652
}
666653

667654
if (existingResult?.status === 'in-progress') {
668-
if (inProgressPolicy === 'skip') {
669-
logger.info(`Skipping duplicate of in-progress operation: ${claimResult.normalizedKey}`)
670-
return { outcome: 'in-progress' }
671-
}
672-
logger.info(`Waiting for in-progress operation: ${claimResult.normalizedKey}`)
673-
const result = await this.waitForResult<T>(
674-
claimResult.normalizedKey,
675-
claimResult.storageMethod,
655+
const { normalizedKey, storageMethod } = claimResult
656+
const deadline =
676657
existingResult.inProgressExpiresAt ??
677-
(existingResult.startedAt ?? Date.now()) + this.config.inProgressTtlSeconds * 1000
678-
)
679-
return { outcome: 'resolved', result }
658+
(existingResult.startedAt ?? Date.now()) + this.config.inProgressTtlSeconds * 1000
659+
return {
660+
outcome: 'in-progress',
661+
wait: () => {
662+
logger.info(`Waiting for in-progress operation: ${normalizedKey}`)
663+
return this.waitForResult<T>(normalizedKey, storageMethod, deadline)
664+
},
665+
}
680666
}
681667

682668
if (existingResult) {

‎scripts/check-explicit-any.baseline.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@
210210
"apps/sim/lib/content/mdx.tsx": 15,
211211
"apps/sim/lib/content/registry-factory.ts": 3,
212212
"apps/sim/lib/core/config/redis.ts": 1,
213-
"apps/sim/lib/core/idempotency/service.ts": 7,
213+
"apps/sim/lib/core/idempotency/service.ts": 6,
214214
"apps/sim/lib/core/security/redaction.ts": 6,
215215
"apps/sim/lib/core/utils/display-filters.ts": 11,
216216
"apps/sim/lib/core/utils/response-format.ts": 10,

0 commit comments

Comments
 (0)