@@ -21,13 +21,15 @@ export interface ConfigStore<T> {
21
21
versionLabel ?: string ;
22
22
}
23
23
24
+ export interface Outcome {
25
+ isInitiallySuccessful : boolean ;
26
+ error ?: Error ;
27
+ }
28
+
24
29
type PollingPhase = 'ready' | 'starting' | 'active' | 'stopped' ;
25
30
26
- /**
27
- * Starts polling immediately upon construction.
28
- */
29
31
export class Poller < T > {
30
- private readonly DEFAULT_POLL_INTERVAL_SECONDS = 30 ;
32
+ private readonly DEFAULT_POLL_INTERVAL_SECONDS = 60 ;
31
33
32
34
private readonly config : PollerConfig < T > ;
33
35
@@ -43,17 +45,33 @@ export class Poller<T> {
43
45
constructor ( config : PollerConfig < T > ) {
44
46
this . config = config ;
45
47
48
+ const {
49
+ pollIntervalSeconds,
50
+ sessionConfig : { RequiredMinimumPollIntervalInSeconds : requiredMin } ,
51
+ } = config ;
52
+
53
+ if (
54
+ pollIntervalSeconds &&
55
+ requiredMin &&
56
+ pollIntervalSeconds < requiredMin
57
+ ) {
58
+ throw new Error (
59
+ 'Cannot configure a poll interval shorter than RequiredMinimumPollIntervalInSeconds' ,
60
+ ) ;
61
+ }
62
+
46
63
this . configStringStore = { } ;
47
64
this . configObjectStore = { } ;
48
65
}
49
66
50
- public async start ( ) : Promise < void > {
67
+ public async start ( ) : Promise < Outcome > {
51
68
if ( this . pollingPhase != 'ready' ) {
52
69
throw new Error ( 'Can only call start() once for an instance of Poller!' ) ;
53
70
}
54
71
this . pollingPhase = 'starting' ;
55
- await this . startPolling ( ) ;
72
+ const result = await this . startPolling ( ) ;
56
73
this . pollingPhase = 'active' ;
74
+ return result ;
57
75
}
58
76
59
77
public stop ( ) : void {
@@ -94,25 +112,33 @@ export class Poller<T> {
94
112
return this . configObjectStore ;
95
113
}
96
114
97
- private async startPolling ( ) : Promise < void > {
115
+ private async startPolling ( ) : Promise < Outcome > {
98
116
const { dataClient, sessionConfig } = this . config ;
99
117
100
118
const startCommand = new StartConfigurationSessionCommand ( sessionConfig ) ;
101
- const result = await dataClient . send ( startCommand ) ;
102
119
103
- if ( ! result . InitialConfigurationToken ) {
104
- throw new Error (
105
- 'Missing configuration token from AppConfig StartConfigurationSession response' ,
106
- ) ;
107
- }
120
+ try {
121
+ const result = await dataClient . send ( startCommand ) ;
108
122
109
- this . configurationToken = result . InitialConfigurationToken ;
123
+ if ( ! result . InitialConfigurationToken ) {
124
+ throw new Error (
125
+ 'Missing configuration token from AppConfig StartConfigurationSession response' ,
126
+ ) ;
127
+ }
128
+
129
+ this . configurationToken = result . InitialConfigurationToken ;
110
130
111
- await this . fetchLatestConfiguration ( ) ;
131
+ return await this . fetchLatestConfiguration ( ) ;
132
+ } catch ( e ) {
133
+ return {
134
+ isInitiallySuccessful : false ,
135
+ error : e ,
136
+ } ;
137
+ }
112
138
}
113
139
114
- private async fetchLatestConfiguration ( ) : Promise < void > {
115
- const { dataClient, logger } = this . config ;
140
+ private async fetchLatestConfiguration ( ) : Promise < Outcome > {
141
+ const { dataClient, logger, pollIntervalSeconds } = this . config ;
116
142
117
143
const getCommand = new GetLatestConfigurationCommand ( {
118
144
ConfigurationToken : this . configurationToken ,
@@ -129,13 +155,9 @@ export class Poller<T> {
129
155
this . configStringStore . errorCausingStaleValue = e ;
130
156
this . configObjectStore . errorCausingStaleValue = e ;
131
157
132
- if ( this . pollingPhase === 'starting' ) {
133
- // If we're part of the initial startup sequence, fail fast.
134
- throw e ;
135
- }
136
-
137
158
logger ?.(
138
- 'Values have gone stale, will wait and then start a new configuration session in response to error:' ,
159
+ `Failed to get value from AppConfig during ${ this . pollingPhase } phase!` +
160
+ `Will wait ${ pollIntervalSeconds } s and then start a new configuration session in response to error:` ,
139
161
e ,
140
162
) ;
141
163
@@ -144,9 +166,12 @@ export class Poller<T> {
144
166
'Starting new configuration session in hopes of recovering...' ,
145
167
) ;
146
168
this . startPolling ( ) ;
147
- } , this . config . pollIntervalSeconds * 1000 ) ;
169
+ } , pollIntervalSeconds * 1000 ) ;
148
170
149
- return ;
171
+ return {
172
+ isInitiallySuccessful : false ,
173
+ error : e ,
174
+ } ;
150
175
}
151
176
152
177
const nextIntervalInSeconds = this . getNextIntervalInSeconds (
@@ -156,6 +181,10 @@ export class Poller<T> {
156
181
this . timeoutHandle = setTimeout ( ( ) => {
157
182
this . fetchLatestConfiguration ( ) ;
158
183
} , nextIntervalInSeconds * 1000 ) ;
184
+
185
+ return {
186
+ isInitiallySuccessful : true ,
187
+ } ;
159
188
}
160
189
161
190
private processGetResponse (
@@ -210,6 +239,12 @@ export class Poller<T> {
210
239
}
211
240
212
241
private getNextIntervalInSeconds ( awsSuggestedSeconds ?: number ) : number {
242
+ const { pollIntervalSeconds } = this . config ;
243
+
244
+ if ( awsSuggestedSeconds && pollIntervalSeconds ) {
245
+ return Math . max ( awsSuggestedSeconds , pollIntervalSeconds ) ;
246
+ }
247
+
213
248
return (
214
249
this . config . pollIntervalSeconds ||
215
250
awsSuggestedSeconds ||
0 commit comments