Skip to content

Commit f31aa42

Browse files
deyaaeldeenxirzec
andauthored
[Text Analytics] Fix rate limit reached issue (Azure#19249)
* [Text Analytics] Fix rate limit reached issue * create an options bag for the throttling retry policy * use the public options to configure the max retries instead * simplify the retry options type * Update sdk/textanalytics/ai-text-analytics/test/public/utils/recordedClient.ts Co-authored-by: Jeff Fisher <xirzec@xirzec.com> * edit the comment * Update sdk/core/core-rest-pipeline/CHANGELOG.md Co-authored-by: Jeff Fisher <xirzec@xirzec.com> Co-authored-by: Jeff Fisher <xirzec@xirzec.com>
1 parent 12b1941 commit f31aa42

File tree

11 files changed

+89
-26
lines changed

11 files changed

+89
-26
lines changed

sdk/core/core-rest-pipeline/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Release History
22

3-
## 1.3.3 (Unreleased)
3+
## 1.4.0 (Unreleased)
44

55
### Features Added
66

77
- The `bearerTokenAuthenticationPolicy` now accepts a logger.
88
- Changed behavior when sending HTTP headers to preserve the original casing of header names. Iterating over `HttpHeaders` now keeps the original name casing. There is also a new `preserveCase` option for `HttpHeaders.toJSON()`. See [PR #18517](https://github.com/Azure/azure-sdk-for-js/pull/18517)
9+
- The count for how many retries in the `throttlingRetryPolicy` policy can now be configured.
910

1011
### Breaking Changes
1112

sdk/core/core-rest-pipeline/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@azure/core-rest-pipeline",
3-
"version": "1.3.3",
3+
"version": "1.4.0",
44
"description": "Isomorphic client library for making HTTP requests in node.js and browser.",
55
"sdk-type": "client",
66
"main": "dist/index.js",

sdk/core/core-rest-pipeline/review/core-rest-pipeline.api.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export interface Pipeline {
178178
export interface PipelineOptions {
179179
proxyOptions?: ProxySettings;
180180
redirectOptions?: RedirectPolicyOptions;
181-
retryOptions?: ExponentialRetryPolicyOptions;
181+
retryOptions?: PipelineRetryOptions;
182182
userAgentOptions?: UserAgentPolicyOptions;
183183
}
184184

@@ -242,6 +242,13 @@ export interface PipelineResponse {
242242
status: number;
243243
}
244244

245+
// @public
246+
export interface PipelineRetryOptions {
247+
maxRetries?: number;
248+
maxRetryDelayInMs?: number;
249+
retryDelayInMs?: number;
250+
}
251+
245252
// @public
246253
export function proxyPolicy(proxySettings?: ProxySettings | undefined, options?: {
247254
customNoProxyList?: string[];
@@ -323,11 +330,16 @@ export interface SystemErrorRetryPolicyOptions {
323330
}
324331

325332
// @public
326-
export function throttlingRetryPolicy(): PipelinePolicy;
333+
export function throttlingRetryPolicy(options?: ThrottlingRetryPolicyOptions): PipelinePolicy;
327334

328335
// @public
329336
export const throttlingRetryPolicyName = "throttlingRetryPolicy";
330337

338+
// @public
339+
export interface ThrottlingRetryPolicyOptions {
340+
maxRetries?: number;
341+
}
342+
331343
// @public
332344
export function tracingPolicy(options?: TracingPolicyOptions): PipelinePolicy;
333345

sdk/core/core-rest-pipeline/src/createPipelineFromOptions.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
// Licensed under the MIT license.
33

44
import { ProxySettings } from ".";
5+
import { PipelineRetryOptions } from "./interfaces";
56
import { Pipeline, createEmptyPipeline } from "./pipeline";
67
import { decompressResponsePolicy } from "./policies/decompressResponsePolicy";
7-
import {
8-
exponentialRetryPolicy,
9-
ExponentialRetryPolicyOptions
10-
} from "./policies/exponentialRetryPolicy";
8+
import { exponentialRetryPolicy } from "./policies/exponentialRetryPolicy";
119
import { formDataPolicy } from "./policies/formDataPolicy";
1210
import { logPolicy, LogPolicyOptions } from "./policies/logPolicy";
1311
import { proxyPolicy } from "./policies/proxyPolicy";
@@ -27,7 +25,7 @@ export interface PipelineOptions {
2725
/**
2826
* Options that control how to retry failed requests.
2927
*/
30-
retryOptions?: ExponentialRetryPolicyOptions;
28+
retryOptions?: PipelineRetryOptions;
3129

3230
/**
3331
* Options to configure a proxy for outgoing requests.
@@ -72,7 +70,7 @@ export function createPipelineFromOptions(options: InternalPipelineOptions): Pip
7270
pipeline.addPolicy(tracingPolicy(options.userAgentOptions));
7371
pipeline.addPolicy(userAgentPolicy(options.userAgentOptions));
7472
pipeline.addPolicy(setClientRequestIdPolicy());
75-
pipeline.addPolicy(throttlingRetryPolicy(), { phase: "Retry" });
73+
pipeline.addPolicy(throttlingRetryPolicy(options.retryOptions), { phase: "Retry" });
7674
pipeline.addPolicy(systemErrorRetryPolicy(options.retryOptions), { phase: "Retry" });
7775
pipeline.addPolicy(exponentialRetryPolicy(options.retryOptions), { phase: "Retry" });
7876
pipeline.addPolicy(redirectPolicy(options.redirectOptions), { afterPhase: "Retry" });

sdk/core/core-rest-pipeline/src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export {
1515
RawHttpHeaders,
1616
RawHttpHeadersInput,
1717
TransferProgressEvent,
18-
RequestBodyType
18+
RequestBodyType,
19+
PipelineRetryOptions
1920
} from "./interfaces";
2021
export {
2122
AddPolicyOptions as AddPipelineOptions,
@@ -58,7 +59,11 @@ export {
5859
SystemErrorRetryPolicyOptions,
5960
systemErrorRetryPolicyName
6061
} from "./policies/systemErrorRetryPolicy";
61-
export { throttlingRetryPolicy, throttlingRetryPolicyName } from "./policies/throttlingRetryPolicy";
62+
export {
63+
throttlingRetryPolicy,
64+
throttlingRetryPolicyName,
65+
ThrottlingRetryPolicyOptions
66+
} from "./policies/throttlingRetryPolicy";
6267
export { tracingPolicy, tracingPolicyName, TracingPolicyOptions } from "./policies/tracingPolicy";
6368
export {
6469
userAgentPolicy,

sdk/core/core-rest-pipeline/src/interfaces.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,26 @@ export type FormDataValue = string | Blob;
294294
* A simple object that provides form data, as if from a browser form.
295295
*/
296296
export type FormDataMap = { [key: string]: FormDataValue | FormDataValue[] };
297+
298+
/**
299+
* Options that control how to retry failed requests.
300+
*/
301+
export interface PipelineRetryOptions {
302+
/**
303+
* The maximum number of retry attempts. Defaults to 10.
304+
*/
305+
maxRetries?: number;
306+
307+
/**
308+
* The amount of delay in milliseconds between retry attempts. Defaults to 1000
309+
* (1 second). The delay increases exponentially with each retry up to a maximum
310+
* specified by maxRetryDelayInMs.
311+
*/
312+
retryDelayInMs?: number;
313+
314+
/**
315+
* The maximum delay in milliseconds allowed before retrying an operation. Defaults
316+
* to 64000 (64 seconds).
317+
*/
318+
maxRetryDelayInMs?: number;
319+
}

sdk/core/core-rest-pipeline/src/policies/throttlingRetryPolicy.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,17 @@ export const throttlingRetryPolicyName = "throttlingRetryPolicy";
1313
/**
1414
* Maximum number of retries for the throttling retry policy
1515
*/
16-
export const DEFAULT_CLIENT_MAX_RETRY_COUNT = 3;
16+
export const DEFAULT_CLIENT_MAX_RETRY_COUNT = 10;
17+
18+
/**
19+
* Options that control how to retry failed requests.
20+
*/
21+
export interface ThrottlingRetryPolicyOptions {
22+
/**
23+
* The maximum number of retry attempts. Defaults to 10.
24+
*/
25+
maxRetries?: number;
26+
}
1727

1828
/**
1929
* A policy that retries when the server sends a 429 response with a Retry-After header.
@@ -22,14 +32,17 @@ export const DEFAULT_CLIENT_MAX_RETRY_COUNT = 3;
2232
* https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-request-limits,
2333
* https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits and
2434
* https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/troubleshooting-throttling-errors
35+
*
36+
* @param options - Options that configure retry logic.
2537
*/
26-
export function throttlingRetryPolicy(): PipelinePolicy {
38+
export function throttlingRetryPolicy(options: ThrottlingRetryPolicyOptions = {}): PipelinePolicy {
2739
return {
2840
name: throttlingRetryPolicyName,
2941
async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
3042
let response = await next(request);
43+
const maxRetryCount = options.maxRetries ?? DEFAULT_CLIENT_MAX_RETRY_COUNT;
3144

32-
for (let count = 0; count < DEFAULT_CLIENT_MAX_RETRY_COUNT; count++) {
45+
for (let count = 0; count < maxRetryCount; count++) {
3346
if (response.status !== 429 && response.status !== 503) {
3447
return response;
3548
}

sdk/core/core-rest-pipeline/test/throttlingRetryPolicy.spec.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
createHttpHeaders,
1212
throttlingRetryPolicy
1313
} from "../src";
14+
import { DEFAULT_CLIENT_MAX_RETRY_COUNT } from "../src/policies/throttlingRetryPolicy";
1415

1516
describe("throttlingRetryPolicy", function() {
1617
afterEach(function() {
@@ -177,7 +178,7 @@ describe("throttlingRetryPolicy", function() {
177178
clock.restore();
178179
});
179180

180-
it("It should retry up to three times", async function(this: Context) {
181+
it("It should retry up to the default max retries", async function(this: Context) {
181182
const clock = sinon.useFakeTimers();
182183

183184
const request = createPipelineRequest({
@@ -193,11 +194,12 @@ describe("throttlingRetryPolicy", function() {
193194

194195
const policy = throttlingRetryPolicy();
195196
const next = sinon.stub<Parameters<SendRequest>, ReturnType<SendRequest>>();
196-
next.onCall(0).resolves(retryResponse);
197-
next.onCall(1).resolves(retryResponse);
198-
next.onCall(2).resolves(retryResponse);
197+
let i = 0;
198+
for (; i < DEFAULT_CLIENT_MAX_RETRY_COUNT; ++i) {
199+
next.onCall(i).resolves(retryResponse);
200+
}
199201
// This one should be returned
200-
next.onCall(3).resolves({
202+
next.onCall(i).resolves({
201203
headers: createHttpHeaders({
202204
"Retry-After": "1",
203205
"final-response": "final-response"
@@ -207,7 +209,7 @@ describe("throttlingRetryPolicy", function() {
207209
});
208210

209211
const promise = policy.sendRequest(request, next);
210-
await clock.tickAsync(3000);
212+
await clock.tickAsync(i * 1000);
211213
const response = await promise;
212214
assert.equal(response.status, 503);
213215
assert.equal(response.headers.get("final-response"), "final-response");

sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -295,20 +295,20 @@ describe("ManagedIdentityCredential", function() {
295295

296296
it("doesn't try IMDS endpoint again once it can't be detected", async function() {
297297
const credential = new ManagedIdentityCredential("errclient");
298+
const DEFAULT_CLIENT_MAX_RETRY_COUNT = 10;
298299
const authDetails = await sendCredentialRequests({
299300
scopes: ["scopes"],
300301
credential,
301302
insecureResponses: [
302303
// Satisfying the ping
303304
createResponse(200),
304305
// Retries until exhaustion
305-
createResponse(503, {}, { "Retry-After": "2" }),
306-
createResponse(503, {}, { "Retry-After": "2" }),
307-
createResponse(503, {}, { "Retry-After": "2" }),
308-
createResponse(503, {}, { "Retry-After": "2" })
306+
...Array(DEFAULT_CLIENT_MAX_RETRY_COUNT + 1).fill(
307+
createResponse(503, {}, { "Retry-After": "2" })
308+
)
309309
]
310310
});
311-
assert.equal(authDetails.requests.length, 5);
311+
assert.equal(authDetails.requests.length, DEFAULT_CLIENT_MAX_RETRY_COUNT + 2);
312312
assert.ok(authDetails.error!.message.indexOf("authentication failed") > -1);
313313

314314
await testContext.restore();

sdk/textanalytics/ai-text-analytics/src/util.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ export function compileError(errorResponse: unknown): any {
190190
};
191191
statusCode: number;
192192
};
193+
if (!castErrorResponse.response) {
194+
throw errorResponse;
195+
}
193196
const topLevelError = castErrorResponse.response.parsedBody?.error;
194197
if (!topLevelError) return errorResponse;
195198
let errorMessage = topLevelError.message || "";

0 commit comments

Comments
 (0)