Read this article for more details:
AWS Exponential Backoff and Jitter
npm i rx-retry
import { resolveWithRetry, ResolveRetryConfig, retryBackoff } from 'rx-retry'
import { throwError } from 'rxjs'
// Use on a promise
;(() => {
const prm = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Promise error'))
}, 1000)
})
const configuration: ResolveRetryConfig = {
timeoutTime: 5000, // set timeout to fail the promise and retry, default is 0
useJitter: true, // backoff strategy with random + exponantial delay, default is true
retryStrategy: {
initialInterval: 1000, // ms
maxRetries: 3,
onRetry: (attempt: number, error: Error) => {
console.log('Log this - retry') // adding action on retry
},
maxInterval: 10000, // ms
shouldRetry: (error) => true, // check if retry needed, default is always true
},
}
const res = await resolveWithRetry(prm, configuration)
})()
// Use on Observable, will be converted into promise
;(() => {
const obs = throwError('Observable error')
const configuration: ResolveRetryConfig = {
timeoutTime: 5000, // set timeout to fail the promise and retry, default is 0
useJitter: true, // backoff strategy with random + exponantial delay, default is true
retryStrategy: {
initialInterval: 1000, // ms
maxRetries: 3,
onRetry: (attempt: number, error: Error) => {
console.log('Log this - retry') // adding action on retry
},
maxInterval: 10000, // ms
shouldRetry: (error) => true, // check if retry needed, default is always true
},
}
const res = await resolveWithRetry(obs, configuration)
})()
// Using the custom operator function in a pipe
;(() => {
const obs = throwError('Observable error')
obs.pipe(
retryBackoff({
initialInterval: 1000,
maxInterval: 10000,
maxRetries: 5,
}),
)
})()
import { Module } from '@nestjs/common'
import { TestingService } from './testing.service'
import { RxRetryModule } from 'rx-retry'
@Module({
imports: [
RxRetryModule.register({
timeoutTime: 5000, // set timeout to fail the promise and retry, default is 0
useJitter: true, // backoff strategy with random + exponantial delay, default is true
retryStrategy: {
initialInterval: 1000, // ms
maxRetries: 3,
maxInterval: 10000, // ms
shouldRetry: (error) => true, // check if retry needed, default is always true
},
isGlobal: true, // set module as global
}),
],
providers: [TestingService],
})
export class TestingModule {}
import { Module } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { TestingService } from './testing.service'
import { RxRetryModule } from 'rx-retry'
@Module({
imports: [
// setup for config module
ConfigModule.forRoot({
isGlobal: true,
cache: true,
}),
RxRetryModule.registerAsync({
inject: [ConfigService],
isGlobal: true, // set module as global
useFactory: async (conf: ConfigService) => {
const configuration = {
timeoutTime: +conf.get('timeoutTime'), // set timeout to fail the promise and retry, default is 0
useJitter: !!conf.get('useJitter'), // backoff strategy with random + exponantial delay, default is true
retryStrategy: {
initialInterval: +conf.get('initialInterval'), // ms
maxRetries: +conf.get('maxRetries'),
maxInterval: 10000, // ms
},
}
return configuration
},
}),
],
providers: [TestingService],
})
export class TestingModule {}
import { Injectable } from '@nestjs/common'
import { RxRetryService } from 'rx-retry'
import { catchError, from, Observable, of, throwError } from 'rxjs'
@Injectable()
export class TestingService {
constructor(private readonly rxRetry: RxRetryService) {}
public resolveWithRetry() {
return this.rxRetry.resolveWithRetry(this._getPromise())
}
// run over only for loggerInstance, passing it to log with it
public runOverConfiguration() {
return this.rxRetry.resolveWithRetry(this._getPromise(), {
retryStrategy: {
onRetry: (attempt: number, error: Error) => {
this.logger.log(`Retry attempt ${attempt}`)
},
},
})
}
// Resolve Obsevable into promise with global config
public resolveWithRetryObs() {
return this.rxRetry.resolveWithRetry(this._getObs())
}
// Using the basic operator
public resolveWithRetryObsPipe() {
return this._getObs().pipe(
retryBackoff({
initialInterval: 1000,
maxInterval: 10000,
maxRetries: 5,
onRetry: (attempt: number, error: Error) => {
console.log('attempt :>> ', attempt)
},
}),
)
}
// Run over part of the main configuration
public resolveWithRetryObsGlobal() {
return this._getObs().pipe(
this.rxRetry.resolveWithRetryOperator({
initialInterval: 1000,
onRetry: (attempt: number, error: Error) => {
this.logger.debug('attempt :>> ', attempt)
},
}),
)
}
private _getObs() {
return throwError('Observable error')
}
private _getPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const isOk = Math.random() > 0.5
if (isOk) {
resolve('Promise resolved')
return
}
reject('Promise rejected')
}, 1000)
})
}
}