1
1
import { logger , field } from "@coder/logger" ;
2
2
import { NotificationService , INotificationHandle , INotificationService , Severity } from "./fill/notification" ;
3
3
4
+ // tslint:disable no-any can have different return values
5
+
4
6
interface IRetryItem {
7
+ /**
8
+ * How many times this item has been retried.
9
+ */
5
10
count ?: number ;
6
- delay ?: number ; // In seconds.
7
- end ?: number ; // In ms.
8
- fn ( ) : any | Promise < any > ; // tslint:disable-line no-any can have different return values
11
+
12
+ /**
13
+ * In seconds.
14
+ */
15
+ delay ?: number ;
16
+
17
+ /**
18
+ * In milliseconds.
19
+ */
20
+ end ?: number ;
21
+
22
+ /**
23
+ * Function to run when retrying.
24
+ */
25
+ fn ( ) : any ;
26
+
27
+ /**
28
+ * Timer for running this item.
29
+ */
9
30
timeout ?: number | NodeJS . Timer ;
31
+
32
+ /**
33
+ * Whether the item is retrying or waiting to retry.
34
+ */
10
35
running ?: boolean ;
11
- showInNotification : boolean ;
36
+ }
37
+
38
+ /**
39
+ * An retry-able instance.
40
+ */
41
+ export interface RetryInstance {
42
+ /**
43
+ * Run this retry.
44
+ */
45
+ run ( error ?: Error ) : void ;
46
+
47
+ /**
48
+ * Block on this instance.
49
+ */
50
+ block ( ) : void ;
51
+ }
52
+
53
+ /**
54
+ * A retry-able instance that doesn't use a promise so it must be manually
55
+ * ran again on failure and recovered on success.
56
+ */
57
+ export interface ManualRetryInstance extends RetryInstance {
58
+ /**
59
+ * Mark this item as recovered.
60
+ */
61
+ recover ( ) : void ;
12
62
}
13
63
14
64
/**
@@ -21,7 +71,7 @@ interface IRetryItem {
21
71
* to the user explaining what is happening with an option to immediately retry.
22
72
*/
23
73
export class Retry {
24
- private items = new Map < string , IRetryItem > ( ) ;
74
+ private readonly items = new Map < string , IRetryItem > ( ) ;
25
75
26
76
// Times are in seconds.
27
77
private readonly retryMinDelay = 1 ;
@@ -50,13 +100,54 @@ export class Retry {
50
100
}
51
101
52
102
/**
53
- * Block retries when we know they will fail (for example when starting Wush
54
- * back up). If a name is passed, that service will still be allowed to retry
103
+ * Register a function to retry that starts/connects to a service.
104
+ *
105
+ * The service is automatically retried or recovered when the promise resolves
106
+ * or rejects. If the service dies after starting, it must be manually
107
+ * retried.
108
+ */
109
+ public register ( name : string , fn : ( ) => Promise < any > ) : RetryInstance ;
110
+ /**
111
+ * Register a function to retry that starts/connects to a service.
112
+ *
113
+ * Must manually retry if it fails to start again or dies after restarting and
114
+ * manually recover if it succeeds in starting again.
115
+ */
116
+ public register ( name : string , fn : ( ) => any ) : ManualRetryInstance ;
117
+ /**
118
+ * Register a function to retry that starts/connects to a service.
119
+ */
120
+ public register ( name : string , fn : ( ) => any ) : RetryInstance | ManualRetryInstance {
121
+ if ( this . items . has ( name ) ) {
122
+ throw new Error ( `"${ name } " is already registered` ) ;
123
+ }
124
+ this . items . set ( name , { fn } ) ;
125
+
126
+ return {
127
+ block : ( ) : void => this . block ( name ) ,
128
+ run : ( error ?: Error ) : void => this . run ( name , error ) ,
129
+ recover : ( ) : void => this . recover ( name ) ,
130
+ } ;
131
+ }
132
+
133
+ /**
134
+ * Un-register a function to retry.
135
+ */
136
+ public unregister ( name : string ) : void {
137
+ if ( ! this . items . has ( name ) ) {
138
+ throw new Error ( `"${ name } " is not registered` ) ;
139
+ }
140
+ this . items . delete ( name ) ;
141
+ }
142
+
143
+ /**
144
+ * Block retries when we know they will fail (for example when the socket is
145
+ * down ). If a name is passed, that service will still be allowed to retry
55
146
* (unless we have already blocked).
56
147
*
57
148
* Blocking without a name will override a block with a name.
58
149
*/
59
- public block ( name ?: string ) : void {
150
+ private block ( name ?: string ) : void {
60
151
if ( ! this . blocked || ! name ) {
61
152
this . blocked = name || true ;
62
153
this . items . forEach ( ( item ) => {
@@ -68,7 +159,7 @@ export class Retry {
68
159
/**
69
160
* Unblock retries and run any that are pending.
70
161
*/
71
- public unblock ( ) : void {
162
+ private unblock ( ) : void {
72
163
this . blocked = false ;
73
164
this . items . forEach ( ( item , name ) => {
74
165
if ( item . running ) {
@@ -77,35 +168,10 @@ export class Retry {
77
168
} ) ;
78
169
}
79
170
80
- /**
81
- * Register a function to retry that starts/connects to a service.
82
- *
83
- * If the function returns a promise, it will automatically be retried,
84
- * recover, & unblock after calling `run` once (otherwise they need to be
85
- * called manually).
86
- */
87
- // tslint:disable-next-line no-any can have different return values
88
- public register ( name : string , fn : ( ) => any | Promise < any > , showInNotification : boolean = true ) : void {
89
- if ( this . items . has ( name ) ) {
90
- throw new Error ( `"${ name } " is already registered` ) ;
91
- }
92
- this . items . set ( name , { fn, showInNotification } ) ;
93
- }
94
-
95
- /**
96
- * Unregister a function to retry.
97
- */
98
- public unregister ( name : string ) : void {
99
- if ( ! this . items . has ( name ) ) {
100
- throw new Error ( `"${ name } " is not registered` ) ;
101
- }
102
- this . items . delete ( name ) ;
103
- }
104
-
105
171
/**
106
172
* Retry a service.
107
173
*/
108
- public run ( name : string , error ?: Error ) : void {
174
+ private run ( name : string , error ?: Error ) : void {
109
175
if ( ! this . items . has ( name ) ) {
110
176
throw new Error ( `"${ name } " is not registered` ) ;
111
177
}
@@ -149,7 +215,7 @@ export class Retry {
149
215
/**
150
216
* Reset a service after a successfully recovering.
151
217
*/
152
- public recover ( name : string ) : void {
218
+ private recover ( name : string ) : void {
153
219
if ( ! this . items . has ( name ) ) {
154
220
throw new Error ( `"${ name } " is not registered` ) ;
155
221
}
@@ -191,9 +257,9 @@ export class Retry {
191
257
if ( this . blocked === name ) {
192
258
this . unblock ( ) ;
193
259
}
194
- } ) . catch ( ( ) => {
260
+ } ) . catch ( ( error ) => {
195
261
endItem ( ) ;
196
- this . run ( name ) ;
262
+ this . run ( name , error ) ;
197
263
} ) ;
198
264
} else {
199
265
endItem ( ) ;
@@ -214,8 +280,7 @@ export class Retry {
214
280
215
281
const now = Date . now ( ) ;
216
282
const items = Array . from ( this . items . entries ( ) ) . filter ( ( [ _ , item ] ) => {
217
- return item . showInNotification
218
- && typeof item . end !== "undefined"
283
+ return typeof item . end !== "undefined"
219
284
&& item . end > now
220
285
&& item . delay && item . delay >= this . notificationThreshold ;
221
286
} ) . sort ( ( a , b ) => {
0 commit comments