Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: network error tolerance #513

Merged
merged 4 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/client/metadataTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { MetadataConverter, SfdxFileFormat } from '../convert';
import { MetadataTransferError } from '../errors';
import { ComponentSet } from '../collections';
import { AsyncResult, MetadataRequestStatus, MetadataTransferResult, RequestStatus } from './types';

export interface MetadataTransferOptions {
usernameOrConnection: string | Connection;
components?: ComponentSet;
Expand Down Expand Up @@ -191,10 +190,25 @@ export abstract class MetadataTransfer<Status extends MetadataRequestStatus, Res
completed = true;
this.canceled = false;
} else {
mdapiStatus = await this.checkStatus();
completed = mdapiStatus?.done;
if (!completed) {
this.event.emit('update', mdapiStatus);
try {
mdapiStatus = await this.checkStatus();
completed = mdapiStatus?.done;
if (!completed) {
this.event.emit('update', mdapiStatus);
}
} catch (e) {
this.logger.error(e);
// tolerate intermittent network errors upto retry limit
if (
['ETIMEDOUT', 'ENOTFOUND', 'ECONNRESET', 'socket hang up'].some((retryableNetworkError) =>
(e as Error).message.includes(retryableNetworkError)
)
) {
this.logger.debug('Network error on the request', e);
await Lifecycle.getInstance().emitWarning('Network error occurred. Continuing to poll.');
return { completed: false };
}
throw e;
}
}
this.logger.debug(`MDAPI status update: ${mdapiStatus.status}`);
Expand Down
13 changes: 12 additions & 1 deletion test/client/metadataTransfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ describe('MetadataTransfer', () => {
const originalError = new Error('whoops');
const expectedError = new MetadataTransferError('md_request_fail', originalError.message);
checkStatus.throws(originalError);

let error: Error;
operation.onError((e) => (error = e));
await operation.pollStatus();
Expand All @@ -224,6 +223,18 @@ describe('MetadataTransfer', () => {
expect(error.message).to.deep.equal(expectedError.message);
});

it('should tolerate network errors', async () => {
const { checkStatus } = operation.lifecycle;
const networkError1 = new Error('something something ETIMEDOUT something');
const networkError2 = new Error('something something ENOTFOUND something');
checkStatus.onFirstCall().throws(networkError1);
checkStatus.onSecondCall().throws(networkError2);
checkStatus.onThirdCall().resolves({ done: true });

await operation.pollStatus();
expect(checkStatus.callCount).to.equal(3);
});

it('should throw wrapped error if there are no error listeners', async () => {
const { checkStatus } = operation.lifecycle;
const originalError = new Error('whoops');
Expand Down