Skip to content

Instantly share code, notes, and snippets.

@ianmartorell
Last active January 22, 2024 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ianmartorell/73a544a7d1a83d305dc846f2ecb6b5f3 to your computer and use it in GitHub Desktop.
Save ianmartorell/73a544a7d1a83d305dc846f2ecb6b5f3 to your computer and use it in GitHub Desktop.
inngest.send with retries
import { retryFunction } from '@lib/utils/retryFunction';
import { ClientOptions, EventSchemas, EventsFromOpts, Inngest } from 'inngest';
import { SendEventPayload } from 'inngest/helpers/types';
import { Events } from './events';
const inngestOptions = {
id: 'my-app',
schemas: new EventSchemas().fromRecord<Events>(),
} satisfies ClientOptions;
export const inngest = new Inngest(inngestOptions);
/**
* Sends an event to Inngest with retry functionality. If the event
* fails to send we will log it without bubbling up the error.
*
* @param event The event payload to send.
*/
export async function sendInngestEvent<
Payload extends SendEventPayload<EventsFromOpts<typeof inngestOptions>>,
>(event: Payload) {
const sendInngestEvent = async () => await inngest.send(event);
try {
await retryFunction(sendInngestEvent);
} catch (error) {
const message = error instanceof Error ? error.message : error;
logger.error(`Failed to send Inngest event: ${message}`, { error, event });
}
}
import { logger } from '@lib/server/logger';
/**
* This function retries a given function a specified number of times.
* The function will be run at most 1 + `retries` times.
*
* Example output:
*
* Running sendInngestEvent: 1 of 4
* Error: fetch failed
* Retrying in 2s... { runCount: 1, retries: 3 }
* Running sendInngestEvent: 2 of 4
* Error: fetch failed
* Retrying in 4s... { runCount: 2, retries: 3 }
* Running sendInngestEvent: 3 of 4
* Error: fetch failed
* Retrying in 6s... { runCount: 3, retries: 3 }
* Running sendInngestEvent: 4 of 4
* Error: fetch failed
* Max retries reached {
* "runCount": 4,
* "retries": 3
* }
*/
export async function retryFunction<T>(
fn: () => T | Promise<T>,
options: { runCount: number; retries: number } = {
runCount: 1,
retries: 3,
}
): Promise<T> {
logger.debug(`Running ${fn.name}: ${options.runCount} of ${options.retries + 1}`);
try {
return await fn();
} catch (error: unknown) {
if (error instanceof Error) {
logger.error(`Error: ${error.message}`);
} else {
logger.error(error);
}
// Throw if we've reached the max number of retries
if (options.runCount >= options.retries + 1) {
logger.error('Max retries reached', options);
throw error || new Error('Max retries reached');
}
// Else retry with an incremental backoff time
const wait = 2000 * options.runCount;
logger.debug(`Retrying in ${wait / 1000}s...`, options);
return new Promise((resolve, reject) => {
setTimeout(
() =>
retryFunction(fn, {
runCount: options.runCount + 1,
retries: options.retries,
}).then(resolve, reject),
wait
);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment