Reputation: 2628
I have a retry decorator that I put over all my http calls for dependency.
Something like:
@retry(3)
callToGoogle(..)
@retry(2)
callToMicrosoft(..)
I need a way to detect which retry attempt is it inside the functions. I don't have the option to add the attempt as a parameter to the function because those functions have optional parameters and different locations in my code call them with different number of parameters.
For some reason when I deploy this source of code to production - the value retryAttempt
in function foo
is undefined.
Weirdly, this works in unit test - meaning the retryAttempt
is populated. I have no idea what this isn't working.
The local test is a node.js process holding my application and the prod environment is kubernetes that runs this process over multiple pods. I don't see a reason why that would matter. Please help me!
Code:
function retrySyncMethod(
times: number,
target: any,
originalMethod: any,
args: any[]): any {
for (var attempt = 1; attempt < times; attempt++) {
const nextAttempt: number = attempt + 1;
target.currAttempt = nextAttempt;
var result = originalMethod.apply(target, args);
return result;
}
}
export function retry(attempts: number):
(target: Object,
propertyKey: string,
descriptor: TypedPropertyDescriptor<any>) => void {
return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> {
var originalMethod = descriptor.value;
descriptor.value = function (...args: any[]): any {
var that = this;
try {
return originalMethod.apply(that, args);
} catch (err) {
return retrySyncMethod(attempts, that, originalMethod, args);
}
};
return descriptor;
};
}
@retry(3)
function foo(host: string, path: string, optionaParam?: null) {
const retryAttempt: number = (<any>this).currAttempt;
if (retryAttempt > 2) {
host = "changing host"
}
console.log("Mock Executing http request and throwing an error to simulate the need of retry");
throw Error("Got 500 - throwing error in order to retry");
}
Upvotes: 1
Views: 1124
Reputation: 1937
How about if you move the change host logic outside of your actual method and let the decorator call it? Like below:
function changeHostIfNecessary(args: any[], attempt: Number, host: string): any[] {
if ( attempt > 3 ) {
const [, ...rest] = args
return [host, rest]
}
return args
}
function retry(attempts: number, fallbackHost: string){
return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> {
var originalMethod = descriptor.value;
descriptor.value = function (...args: any[]): any {
var that = this;
let currentAttempt = 1
do {
try {
const modArgs = changeHostIfNecessary(args, currentAttempt, fallbackHost)
originalMethod.apply(that, modArgs);
} catch (err) {
currentAttempt++
}
} while(currentAttempt <= attempts)
};
return descriptor;
};
}
class Google{
@retry(5, "alternateHost")
public call(host: string, path: string, optionaParam?: null){
console.log(`Calling with host :${host}...`)
throw Error("Simulating a runtime exception.....")
}
}
const g = new Google()
g.call('InitialHost','doc/xyz')
Upvotes: 1