-
Notifications
You must be signed in to change notification settings - Fork 1
/
01_retry.ts
143 lines (121 loc) · 4.25 KB
/
01_retry.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
성공할 때까지 작업을 반복적으로 수행하는 메커니즘에 대한 추상화입니다.
이 모듈은 세 부분으로 나뉩니다:
- 모델
- 원시 요소
- 결합자
*/
// -------------------------------------------------------------------------------------
// 모델
// -------------------------------------------------------------------------------------
export interface RetryStatus {
/** 반복 횟수, 여기서 `0`은 첫 번째 시도 */
readonly iterNumber: number
/** 최근 시도의 지연 시간, 첫 실행에서는 항상 `undefined` 상태가 됩니다. */
readonly previousDelay: number | undefined
}
export const startStatus: RetryStatus = {
iterNumber: 0,
previousDelay: undefined
}
/**
* `RetryPolicy`은 `RetryStatus`를 받는 함수입니다.
* 지연 시간(밀리초)을 반환할 수 있습니다. 반복 횟수는
* 0에서 시작하여 재시도할 때마다 1씩 증가합니다.
* 함수가 *undefined*를 반환하는 것은 재시도 제한에 도달했음을 의미합니다.
*/
export interface RetryPolicy {
(status: RetryStatus): number | undefined
}
// -------------------------------------------------------------------------------------
// 원시 요소
// -------------------------------------------------------------------------------------
/**
* 무제한 재시도와 지속적인 지연
*/
export const constantDelay = (delay: number): RetryPolicy => () => delay
/**
* 최대 'i'회까지만 즉시 다시 시도
*/
export const limitRetries = (i: number): RetryPolicy => (status) =>
status.iterNumber >= i ? undefined : 0
/**
* 반복할 때마다 지연 시간이 기하급수적으로 증가합니다.
* 각 지연 시간은 2배씩 증가합니다.
*/
export const exponentialBackoff = (delay: number): RetryPolicy => (status) =>
delay * Math.pow(2, status.iterNumber)
// -------------------------------------------------------------------------------------
// 결합자
// -------------------------------------------------------------------------------------
/**
* 지정된 정책에 의해 지시될 수 있는 지연에 대한 시간 상한을 설정합니다.
*/
export const capDelay = (maxDelay: number) => (
policy: RetryPolicy
): RetryPolicy => (status) => {
const delay = policy(status)
return delay === undefined ? undefined : Math.min(maxDelay, delay)
}
/**
* 두 정책을 병합합니다.
* **퀴즈**: 두 정책을 병합한다는 것은 무엇을 의미하나요?
*/
export const concat = (second: RetryPolicy) => (
first: RetryPolicy
): RetryPolicy => (status) => {
const delay1 = first(status)
const delay2 = second(status)
if (delay1 !== undefined && delay2 !== undefined) {
return Math.max(delay1, delay2)
}
return undefined
}
// -------------------------------------------------------------------------------------
// 테스트
// -------------------------------------------------------------------------------------
/**
* 상태에 대한 정책을 적용하여 결정 사항을 확인합니다.
*/
export const applyPolicy = (policy: RetryPolicy) => (
status: RetryStatus
): RetryStatus => ({
iterNumber: status.iterNumber + 1,
previousDelay: policy(status)
})
/**
* 모든 중간 결과를 유지하는 정책을 적용합니다.
*/
export const dryRun = (policy: RetryPolicy): ReadonlyArray<RetryStatus> => {
const apply = applyPolicy(policy)
let status: RetryStatus = apply(startStatus)
const out: Array<RetryStatus> = [status]
while (status.previousDelay !== undefined) {
out.push((status = apply(out[out.length - 1])))
}
return out
}
import { pipe } from 'fp-ts/function'
/*
constantDelay(300)
|> concat(exponentialBackoff(200))
|> concat(limitRetries(5))
|> capDelay(2000)
*/
const myPolicy = pipe(
constantDelay(300),
concat(exponentialBackoff(200)),
concat(limitRetries(5)),
capDelay(2000)
)
console.log(dryRun(myPolicy))
/*
[
{ iterNumber: 1, previousDelay: 300 }, <= constantDelay
{ iterNumber: 2, previousDelay: 400 }, <= exponentialBackoff
{ iterNumber: 3, previousDelay: 800 }, <= exponentialBackoff
{ iterNumber: 4, previousDelay: 1600 }, <= exponentialBackoff
{ iterNumber: 5, previousDelay: 2000 }, <= capDelay
{ iterNumber: 6, previousDelay: undefined } <= limitRetries
]
*/