88} from './Exceptions' ;
99import { BasePersistenceLayer , IdempotencyRecord } from './persistence' ;
1010import { IdempotencyConfig } from './IdempotencyConfig' ;
11+ import { MAX_RETRIES } from './constants' ;
1112
1213export class IdempotencyHandler < U > {
1314 private readonly fullFunctionPayload : Record < string , unknown > ;
@@ -36,9 +37,9 @@ export class IdempotencyHandler<U> {
3637 } ) ;
3738 }
3839
39- public determineResultFromIdempotencyRecord (
40+ public static determineResultFromIdempotencyRecord (
4041 idempotencyRecord : IdempotencyRecord
41- ) : Promise < U > | U {
42+ ) : Promise < unknown > | unknown {
4243 if ( idempotencyRecord . getStatus ( ) === IdempotencyRecordStatus . EXPIRED ) {
4344 throw new IdempotencyInconsistentStateError (
4445 'Item has expired during processing and may not longer be valid.'
@@ -61,7 +62,7 @@ export class IdempotencyHandler<U> {
6162 }
6263 }
6364
64- return idempotencyRecord . getResponse ( ) as U ;
65+ return idempotencyRecord . getResponse ( ) ;
6566 }
6667
6768 public async getFunctionResult ( ) : Promise < U > {
@@ -96,26 +97,30 @@ export class IdempotencyHandler<U> {
9697
9798 /**
9899 * Main entry point for the handler
99- * IdempotencyInconsistentStateError can happen under rare but expected cases
100- * when persistent state changes in the small time between put & get requests.
101- * In most cases we can retry successfully on this exception.
100+ *
101+ * In some rare cases, when the persistent state changes in small time
102+ * window, we might get an `IdempotencyInconsistentStateError`. In such
103+ * cases we can safely retry the handling a few times.
102104 */
103105 public async handle ( ) : Promise < U > {
104- const MAX_RETRIES = 2 ;
105- for ( let i = 1 ; i <= MAX_RETRIES ; i ++ ) {
106+ let e ;
107+ for ( let retryNo = 0 ; retryNo <= MAX_RETRIES ; retryNo ++ ) {
106108 try {
107109 return await this . processIdempotency ( ) ;
108- } catch ( e ) {
110+ } catch ( error ) {
109111 if (
110- ! ( e instanceof IdempotencyAlreadyInProgressError ) ||
111- i === MAX_RETRIES
112+ error instanceof IdempotencyInconsistentStateError &&
113+ retryNo < MAX_RETRIES
112114 ) {
113- throw e ;
115+ // Retry
116+ continue ;
114117 }
118+ // Retries exhausted or other error
119+ e = error ;
120+ break ;
115121 }
116122 }
117- /* istanbul ignore next */
118- throw new Error ( 'This should never happen' ) ;
123+ throw e ;
119124 }
120125
121126 public async processIdempotency ( ) : Promise < U > {
@@ -128,7 +133,9 @@ export class IdempotencyHandler<U> {
128133 const idempotencyRecord : IdempotencyRecord =
129134 await this . persistenceStore . getRecord ( this . functionPayloadToBeHashed ) ;
130135
131- return this . determineResultFromIdempotencyRecord ( idempotencyRecord ) ;
136+ return IdempotencyHandler . determineResultFromIdempotencyRecord (
137+ idempotencyRecord
138+ ) as U ;
132139 } else {
133140 throw new IdempotencyPersistenceLayerError ( ) ;
134141 }
0 commit comments