8
8
} from './Exceptions' ;
9
9
import { BasePersistenceLayer , IdempotencyRecord } from './persistence' ;
10
10
import { IdempotencyConfig } from './IdempotencyConfig' ;
11
+ import { MAX_RETRIES } from './constants' ;
11
12
12
13
export class IdempotencyHandler < U > {
13
14
private readonly fullFunctionPayload : Record < string , unknown > ;
@@ -36,9 +37,9 @@ export class IdempotencyHandler<U> {
36
37
} ) ;
37
38
}
38
39
39
- public determineResultFromIdempotencyRecord (
40
+ public static determineResultFromIdempotencyRecord (
40
41
idempotencyRecord : IdempotencyRecord
41
- ) : Promise < U > | U {
42
+ ) : Promise < unknown > | unknown {
42
43
if ( idempotencyRecord . getStatus ( ) === IdempotencyRecordStatus . EXPIRED ) {
43
44
throw new IdempotencyInconsistentStateError (
44
45
'Item has expired during processing and may not longer be valid.'
@@ -61,7 +62,7 @@ export class IdempotencyHandler<U> {
61
62
}
62
63
}
63
64
64
- return idempotencyRecord . getResponse ( ) as U ;
65
+ return idempotencyRecord . getResponse ( ) ;
65
66
}
66
67
67
68
public async getFunctionResult ( ) : Promise < U > {
@@ -96,26 +97,30 @@ export class IdempotencyHandler<U> {
96
97
97
98
/**
98
99
* 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.
102
104
*/
103
105
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 ++ ) {
106
108
try {
107
109
return await this . processIdempotency ( ) ;
108
- } catch ( e ) {
110
+ } catch ( error ) {
109
111
if (
110
- ! ( e instanceof IdempotencyAlreadyInProgressError ) ||
111
- i === MAX_RETRIES
112
+ error instanceof IdempotencyInconsistentStateError &&
113
+ retryNo < MAX_RETRIES
112
114
) {
113
- throw e ;
115
+ // Retry
116
+ continue ;
114
117
}
118
+ // Retries exhausted or other error
119
+ e = error ;
120
+ break ;
115
121
}
116
122
}
117
- /* istanbul ignore next */
118
- throw new Error ( 'This should never happen' ) ;
123
+ throw e ;
119
124
}
120
125
121
126
public async processIdempotency ( ) : Promise < U > {
@@ -128,7 +133,9 @@ export class IdempotencyHandler<U> {
128
133
const idempotencyRecord : IdempotencyRecord =
129
134
await this . persistenceStore . getRecord ( this . functionPayloadToBeHashed ) ;
130
135
131
- return this . determineResultFromIdempotencyRecord ( idempotencyRecord ) ;
136
+ return IdempotencyHandler . determineResultFromIdempotencyRecord (
137
+ idempotencyRecord
138
+ ) as U ;
132
139
} else {
133
140
throw new IdempotencyPersistenceLayerError ( ) ;
134
141
}
0 commit comments