Skip to content

Commit 25359cf

Browse files
authored
Automatically retrying on 503 errors (#556)
* Automatically retrying on 503 errors * Using explicit RetryConfig for tests * Added tests for defaultRetryConfig() * Using defaultRetryConfig() explicitly for clarity
1 parent 84cc273 commit 25359cf

File tree

4 files changed

+118
-65
lines changed

4 files changed

+118
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
apps in a Firebase project
55
- [added] `admin.projectManagement().setDisplayName()` method to update the display name of a
66
Firebase project
7+
- [fixed] The SDK now automatically retries HTTP calls failing due to 503 errors.
78

89
# v8.0.0
910

src/utils/api-request.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,19 @@ export interface RetryConfig {
191191
}
192192

193193
/**
194-
* Default retry configuration for HTTP requests. Retries once on connection reset and timeout errors.
194+
* Default retry configuration for HTTP requests. Retries up to 4 times on connection reset and timeout errors
195+
* as well as HTTP 503 errors. Exposed as a function to ensure that every HttpClient gets its own RetryConfig
196+
* instance.
195197
*/
196-
const DEFAULT_RETRY_CONFIG: RetryConfig = {
197-
maxRetries: 1,
198-
ioErrorCodes: ['ECONNRESET', 'ETIMEDOUT'],
199-
maxDelayInMillis: 60 * 1000,
200-
};
198+
export function defaultRetryConfig(): RetryConfig {
199+
return {
200+
maxRetries: 4,
201+
statusCodes: [503],
202+
ioErrorCodes: ['ECONNRESET', 'ETIMEDOUT'],
203+
backOffFactor: 0.5,
204+
maxDelayInMillis: 60 * 1000,
205+
};
206+
}
201207

202208
/**
203209
* Ensures that the given RetryConfig object is valid.
@@ -233,7 +239,7 @@ function validateRetryConfig(retry: RetryConfig) {
233239

234240
export class HttpClient {
235241

236-
constructor(private readonly retry: RetryConfig = DEFAULT_RETRY_CONFIG) {
242+
constructor(private readonly retry: RetryConfig = defaultRetryConfig()) {
237243
if (this.retry) {
238244
validateRetryConfig(this.retry);
239245
}

test/unit/messaging/messaging.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ function mockTopicSubscriptionRequestWithError(
299299
});
300300
}
301301

302+
function disableRetries(messaging: Messaging) {
303+
(messaging as any).messagingRequestHandler.httpClient.retry = null;
304+
}
302305

303306
describe('Messaging', () => {
304307
let mockApp: FirebaseApp;
@@ -1167,6 +1170,7 @@ describe('Messaging', () => {
11671170
_.forEach(STATUS_CODE_TO_ERROR_MAP, (expectedError, statusCode) => {
11681171
it(`should be rejected given a ${ statusCode } text server response`, () => {
11691172
mockedRequests.push(mockSendRequestWithError(parseInt(statusCode, 10), 'text'));
1173+
disableRetries(messaging);
11701174

11711175
return messaging.sendToDevice(
11721176
mocks.messaging.registrationToken,
@@ -1460,6 +1464,7 @@ describe('Messaging', () => {
14601464
_.forEach(STATUS_CODE_TO_ERROR_MAP, (expectedError, statusCode) => {
14611465
it(`should be rejected given a ${ statusCode } text server response`, () => {
14621466
mockedRequests.push(mockSendRequestWithError(parseInt(statusCode, 10), 'text'));
1467+
disableRetries(messaging);
14631468

14641469
return messaging.sendToDeviceGroup(
14651470
mocks.messaging.notificationKey,
@@ -1710,6 +1715,7 @@ describe('Messaging', () => {
17101715
_.forEach(STATUS_CODE_TO_ERROR_MAP, (expectedError, statusCode) => {
17111716
it(`should be rejected given a ${ statusCode } text server response`, () => {
17121717
mockedRequests.push(mockSendRequestWithError(parseInt(statusCode, 10), 'text'));
1718+
disableRetries(messaging);
17131719

17141720
return messaging.sendToTopic(
17151721
mocks.messaging.topic,
@@ -1928,6 +1934,7 @@ describe('Messaging', () => {
19281934
_.forEach(STATUS_CODE_TO_ERROR_MAP, (expectedError, statusCode) => {
19291935
it(`should be rejected given a ${ statusCode } text server response`, () => {
19301936
mockedRequests.push(mockSendRequestWithError(parseInt(statusCode, 10), 'text'));
1937+
disableRetries(messaging);
19311938

19321939
return messaging.sendToCondition(
19331940
mocks.messaging.condition,
@@ -3398,6 +3405,7 @@ describe('Messaging', () => {
33983405
_.forEach(STATUS_CODE_TO_ERROR_MAP, (expectedError, statusCode) => {
33993406
it(`should be rejected given a ${ statusCode } text server response`, () => {
34003407
mockedRequests.push(mockTopicSubscriptionRequestWithError(methodName, parseInt(statusCode, 10), 'text'));
3408+
disableRetries(messaging);
34013409

34023410
return messagingService[methodName](
34033411
mocks.messaging.registrationToken,

0 commit comments

Comments
 (0)