Skip to content

Commit ee04d60

Browse files
authored
Add retry logic to RestService (#165)
1 parent 76f479c commit ee04d60

File tree

5 files changed

+55
-5
lines changed

5 files changed

+55
-5
lines changed

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemaregistry/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"async-mutex": "^0.5.0",
4545
"avsc": "^5.7.7",
4646
"axios": "^1.7.3",
47+
"axios-retry": "^4.5.0",
4748
"json-stringify-deterministic": "^1.0.12",
4849
"jsonata": "^2.0.5",
4950
"lru-cache": "^10.4.3",

schemaregistry/rest-service.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults } from 'axios';
22
import { OAuthClient } from './oauth/oauth-client';
33
import { RestError } from './rest-error';
4+
import axiosRetry from "axios-retry";
45
/*
56
* Confluent-Schema-Registry-TypeScript - Node.js wrapper for Confluent Schema Registry
67
*
@@ -33,7 +34,6 @@ export interface BearerAuthCredentials {
3334
identityPoolId?: string,
3435
}
3536

36-
//TODO: Consider retry policy, may need additional libraries on top of Axios
3737
export interface ClientConfig {
3838
baseURLs: string[],
3939
cacheCapacity?: number,
@@ -42,6 +42,9 @@ export interface ClientConfig {
4242
createAxiosDefaults?: CreateAxiosDefaults,
4343
basicAuthCredentials?: BasicAuthCredentials,
4444
bearerAuthCredentials?: BearerAuthCredentials,
45+
maxRetries?: number,
46+
retriesWaitMs?: number,
47+
retriesMaxWaitMs?: number,
4548
}
4649

4750
const toBase64 = (str: string): string => Buffer.from(str).toString('base64');
@@ -53,19 +56,38 @@ export class RestService {
5356
private oauthBearer: boolean = false;
5457

5558
constructor(baseURLs: string[], isForward?: boolean, axiosDefaults?: CreateAxiosDefaults,
56-
basicAuthCredentials?: BasicAuthCredentials, bearerAuthCredentials?: BearerAuthCredentials) {
59+
basicAuthCredentials?: BasicAuthCredentials, bearerAuthCredentials?: BearerAuthCredentials,
60+
maxRetries?: number, retriesWaitMs?: number, retriesMaxWaitMs?: number) {
5761
this.client = axios.create(axiosDefaults);
62+
axiosRetry(this.client, {
63+
retries: maxRetries ?? 2,
64+
retryDelay: (retryCount) => {
65+
return this.fullJitter(retriesWaitMs ?? 1000, retriesMaxWaitMs ?? 20000, retryCount - 1)
66+
},
67+
retryCondition: (error) => {
68+
return this.isRetriable(error.response?.status ?? 0);
69+
}
70+
});
5871
this.baseURLs = baseURLs;
5972

6073
if (isForward) {
6174
this.setHeaders({ 'X-Forward': 'true' });
6275
}
6376
this.setHeaders({ 'Content-Type': 'application/vnd.schemaregistry.v1+json' });
64-
77+
6578
this.handleBasicAuth(basicAuthCredentials);
6679
this.handleBearerAuth(bearerAuthCredentials);
6780
}
6881

82+
isRetriable(statusCode: number): boolean {
83+
return statusCode == 408 || statusCode == 429
84+
|| statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 504;
85+
}
86+
87+
fullJitter(baseDelayMs: number, maxDelayMs: number, retriesAttempted: number): number {
88+
return Math.random() * Math.min(maxDelayMs, baseDelayMs * 2 ** retriesAttempted)
89+
}
90+
6991
handleBasicAuth(basicAuthCredentials?: BasicAuthCredentials): void {
7092
if (basicAuthCredentials) {
7193
switch (basicAuthCredentials.credentialsSource) {

schemaregistry/rules/encryption/dekregistry/dekregistry-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ class DekRegistryClient implements DekClient {
7171

7272

7373
this.restService = new RestService(config.baseURLs, config.isForward, config.createAxiosDefaults,
74-
config.basicAuthCredentials, config.bearerAuthCredentials);
74+
config.basicAuthCredentials, config.bearerAuthCredentials,
75+
config.maxRetries, config.retriesWaitMs, config.retriesMaxWaitMs);
7576
this.kekCache = new LRUCache<string, Kek>(cacheOptions);
7677
this.dekCache = new LRUCache<string, Dek>(cacheOptions);
7778
this.kekMutex = new Mutex();

schemaregistry/schemaregistry-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ export class SchemaRegistryClient implements Client {
202202
};
203203

204204
this.restService = new RestService(config.baseURLs, config.isForward, config.createAxiosDefaults,
205-
config.basicAuthCredentials, config.bearerAuthCredentials);
205+
config.basicAuthCredentials, config.bearerAuthCredentials,
206+
config.maxRetries, config.retriesWaitMs, config.retriesMaxWaitMs);
206207

207208
this.schemaToIdCache = new LRUCache(cacheOptions);
208209
this.idToSchemaInfoCache = new LRUCache(cacheOptions);

0 commit comments

Comments
 (0)