diff --git a/plugins/orchestrator-backend/src/service/Helper.test.ts b/plugins/orchestrator-backend/src/service/Helper.test.ts new file mode 100644 index 0000000000..caebd566e9 --- /dev/null +++ b/plugins/orchestrator-backend/src/service/Helper.test.ts @@ -0,0 +1,49 @@ +import { retryAsyncFunction } from './Helper'; + +describe('retryAsyncFunction', () => { + const successfulResponse = 'Success'; + it('should be successful in the first attempt', async () => { + const asyncFnSuccess = jest.fn().mockResolvedValueOnce(successfulResponse); + + const result = await retryAsyncFunction({ + asyncFn: asyncFnSuccess, + maxAttempts: 3, + delayMs: 100, + }); + + expect(result).toBe(successfulResponse); + expect(asyncFnSuccess).toHaveBeenCalledTimes(1); + }); + + it('should throw an error after maximum attempts', async () => { + const asyncFnFailure = jest.fn().mockResolvedValue(undefined); + + await expect( + retryAsyncFunction({ + asyncFn: asyncFnFailure, + maxAttempts: 5, + delayMs: 100, + }), + ).rejects.toThrow(); + + expect(asyncFnFailure).toHaveBeenCalledTimes(5); + }); + + it('should retry until successful after getting some undefined responses', async () => { + const asyncFns = jest + .fn() + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce(successfulResponse); + + const result = await retryAsyncFunction({ + asyncFn: asyncFns, + maxAttempts: 5, + delayMs: 100, + }); + + expect(result).toBe(successfulResponse); + expect(asyncFns).toHaveBeenCalledTimes(4); + }); +}); diff --git a/plugins/orchestrator-backend/src/service/Helper.ts b/plugins/orchestrator-backend/src/service/Helper.ts index c1db42a715..010a1a5438 100644 --- a/plugins/orchestrator-backend/src/service/Helper.ts +++ b/plugins/orchestrator-backend/src/service/Helper.ts @@ -6,21 +6,19 @@ import { Logger } from 'winston'; import os from 'os'; export async function retryAsyncFunction(args: { - asyncFunc: () => Promise; - retries: number; + asyncFn: () => Promise; + maxAttempts: number; delayMs: number; }): Promise { let result: T | undefined; - for (let i = 0; i < args.retries; i++) { - result = await args.asyncFunc(); + for (let i = 0; i < args.maxAttempts; i++) { + result = await args.asyncFn(); if (result !== undefined) { return result; } await new Promise(resolve => setTimeout(resolve, args.delayMs)); } - throw new Error( - `Exceeded maximum number of retries for function ${args.asyncFunc.name}`, - ); + throw new Error('Exceeded maximum number of retries for async function'); } export async function getWorkingDirectory( diff --git a/plugins/orchestrator-backend/src/service/api/v1.ts b/plugins/orchestrator-backend/src/service/api/v1.ts index 341f716085..7540796ca8 100644 --- a/plugins/orchestrator-backend/src/service/api/v1.ts +++ b/plugins/orchestrator-backend/src/service/api/v1.ts @@ -14,7 +14,7 @@ import { DataIndexService } from '../DataIndexService'; import { retryAsyncFunction } from '../Helper'; import { SonataFlowService } from '../SonataFlowService'; -const FETCH_INSTANCE_MAX_RETRIES = 5; +const FETCH_INSTANCE_MAX_ATTEMPTS = 10; const FETCH_INSTANCE_RETRY_DELAY_MS = 1000; export namespace V1 { @@ -138,9 +138,9 @@ export namespace V1 { // Making sure the instance data is available before returning await retryAsyncFunction({ - asyncFunc: () => + asyncFn: () => dataIndexService.fetchProcessInstance(executionResponse.id), - retries: FETCH_INSTANCE_MAX_RETRIES, + maxAttempts: FETCH_INSTANCE_MAX_ATTEMPTS, delayMs: FETCH_INSTANCE_RETRY_DELAY_MS, });