Skip to content

Commit e97744f

Browse files
fix: add variable polling times based on comp. set (#520)
* fix: add variable polling times based on comp. set * chore: review changes Co-authored-by: Shane McLaughlin <[email protected]>
1 parent 94b7b93 commit e97744f

File tree

2 files changed

+93
-5
lines changed

2 files changed

+93
-5
lines changed

src/client/metadataTransfer.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { MetadataConverter, SfdxFileFormat } from '../convert';
1414
import { MetadataTransferError } from '../errors';
1515
import { ComponentSet } from '../collections';
1616
import { AsyncResult, MetadataRequestStatus, MetadataTransferResult, RequestStatus } from './types';
17+
1718
export interface MetadataTransferOptions {
1819
usernameOrConnection: string | Connection;
1920
components?: ComponentSet;
@@ -66,7 +67,7 @@ export abstract class MetadataTransfer<Status extends MetadataRequestStatus, Res
6667
public async pollStatus(options?: Partial<PollingClient.Options>): Promise<Result>;
6768
/**
6869
* Poll for the status of the metadata transfer request.
69-
* Default frequency is 100 ms.
70+
* Default frequency is based on the number of SourceComponents, n, in the transfer, it ranges from 100ms -> n
7071
* Default timeout is 60 minutes.
7172
*
7273
* @param frequency Polling frequency in milliseconds.
@@ -79,7 +80,7 @@ export abstract class MetadataTransfer<Status extends MetadataRequestStatus, Res
7980
timeout?: number
8081
): Promise<Result> {
8182
let pollingOptions: PollingClient.Options = {
82-
frequency: Duration.milliseconds(100),
83+
frequency: Duration.milliseconds(this.calculatePollingFrequency()),
8384
timeout: Duration.minutes(60),
8485
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
8586
poll: this.poll.bind(this),
@@ -92,6 +93,10 @@ export abstract class MetadataTransfer<Status extends MetadataRequestStatus, Res
9293
if (isNumber(timeout)) {
9394
pollingOptions.timeout = Duration.seconds(timeout);
9495
}
96+
// from the overloaded methods, there's a possibility frequency/timeout isn't set
97+
// guarantee frequency and timeout are set
98+
pollingOptions.frequency ??= Duration.milliseconds(this.calculatePollingFrequency());
99+
pollingOptions.timeout ??= Duration.minutes(60);
95100

96101
const pollingClient = await PollingClient.create(pollingOptions);
97102

@@ -216,6 +221,28 @@ export abstract class MetadataTransfer<Status extends MetadataRequestStatus, Res
216221
return { completed, payload: mdapiStatus as unknown as AnyJson };
217222
}
218223

224+
/**
225+
* Based on the source components in the component set, it will return a polling frequency in milliseconds
226+
*/
227+
private calculatePollingFrequency(): number {
228+
const size = this.components?.getSourceComponents().toArray().length || 0;
229+
// take a piece-wise approach to encapsulate discrete deployment sizes in polling frequencies that "feel" good when deployed
230+
if (size === 0) {
231+
// no component set size is possible for retrieve
232+
return 1000;
233+
} else if (size <= 10) {
234+
return 100;
235+
} else if (size <= 50) {
236+
return 250;
237+
} else if (size <= 100) {
238+
return 500;
239+
} else if (size <= 1000) {
240+
return 1000;
241+
} else {
242+
return size;
243+
}
244+
}
245+
219246
public abstract checkStatus(): Promise<Status>;
220247
public abstract cancel(): Promise<void>;
221248
protected abstract pre(): Promise<AsyncResult>;

test/client/metadataTransfer.test.ts

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import { fail } from 'assert';
88
import { createSandbox, SinonStub } from 'sinon';
99
import { MockTestOrgData, testSetup } from '@salesforce/core/lib/testSetup';
10-
import { AuthInfo, Connection } from '@salesforce/core';
10+
import { AuthInfo, Connection, PollingClient } from '@salesforce/core';
1111
import { expect } from 'chai';
12-
import { sleep } from '@salesforce/kit';
12+
import { Duration, sleep } from '@salesforce/kit';
1313
import { ComponentSet } from '../../src';
1414
import { MetadataTransfer } from '../../src/client/metadataTransfer';
1515
import { MetadataRequestStatus, MetadataTransferResult, RequestStatus } from '../../src/client/types';
@@ -163,7 +163,68 @@ describe('MetadataTransfer', () => {
163163
expect(listenerStub.callCount).to.equal(1);
164164
});
165165

166-
it('should wait for polling function to return before queing another', async () => {
166+
it('should calculate polling frequency based on source components, 0 -> 1000', async () => {
167+
const pollingClientStub = env.stub(PollingClient, 'create').resolves(PollingClient.prototype);
168+
env.stub(PollingClient.prototype, 'subscribe').resolves({ status: RequestStatus.Canceled, done: true });
169+
const { checkStatus } = operation.lifecycle;
170+
checkStatus.resolves({ status: RequestStatus.Canceled, done: true });
171+
172+
operation.onCancel(() => listenerStub());
173+
await operation.pollStatus();
174+
175+
expect(pollingClientStub.calledOnce).to.be.true;
176+
expect((pollingClientStub.firstCall.args[0] as { frequency: number }).frequency).to.deep.equal(
177+
Duration.milliseconds(1000)
178+
);
179+
});
180+
181+
it('should calculate polling frequency based on source components, 10 -> 100', async () => {
182+
// @ts-ignore protected member access
183+
env.stub(operation.components, 'getSourceComponents').returns({
184+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
185+
// @ts-ignore only override length attribute
186+
toArray: () => {
187+
return { length: 10 };
188+
},
189+
});
190+
const pollingClientStub = env.stub(PollingClient, 'create').resolves(PollingClient.prototype);
191+
env.stub(PollingClient.prototype, 'subscribe').resolves({ status: RequestStatus.Canceled, done: true });
192+
const { checkStatus } = operation.lifecycle;
193+
checkStatus.resolves({ status: RequestStatus.Canceled, done: true });
194+
195+
operation.onCancel(() => listenerStub());
196+
await operation.pollStatus({ frequency: undefined, timeout: Duration.minutes(60) });
197+
198+
expect(pollingClientStub.calledOnce).to.be.true;
199+
expect((pollingClientStub.firstCall.args[0] as { frequency: number }).frequency).to.deep.equal(
200+
Duration.milliseconds(100)
201+
);
202+
});
203+
204+
it('should calculate polling frequency based on source components, 2520 -> 2520', async () => {
205+
// @ts-ignore protected member access
206+
env.stub(operation.components, 'getSourceComponents').returns({
207+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
208+
// @ts-ignore only override length attribute
209+
toArray: () => {
210+
return { length: 2520 };
211+
},
212+
});
213+
const pollingClientStub = env.stub(PollingClient, 'create').resolves(PollingClient.prototype);
214+
env.stub(PollingClient.prototype, 'subscribe').resolves({ status: RequestStatus.Canceled, done: true });
215+
const { checkStatus } = operation.lifecycle;
216+
checkStatus.resolves({ status: RequestStatus.Canceled, done: true });
217+
218+
operation.onCancel(() => listenerStub());
219+
await operation.pollStatus(undefined, 60);
220+
221+
expect(pollingClientStub.calledOnce).to.be.true;
222+
expect((pollingClientStub.firstCall.args[0] as { frequency: number }).frequency).to.deep.equal(
223+
Duration.milliseconds(2520)
224+
);
225+
});
226+
227+
it('should wait for polling function to return before queuing another', async () => {
167228
const { checkStatus } = operation.lifecycle;
168229
const checkStatusRuntime = 50;
169230
const callOrder: string[] = [];

0 commit comments

Comments
 (0)