pg-transactional-outbox
Version:
A PostgreSQL based transactional outbox and inbox pattern implementation to support exactly once message processing (with at least once message delivery).
56 lines (53 loc) • 2.33 kB
text/typescript
import { isPgSerializationError } from '../common/database';
import { ExtendedError } from '../common/error';
import { FullListenerConfig } from '../common/listener-config';
import { StoredTransactionalMessage } from '../message/transactional-message';
/**
* Decides if a message should be attempted again and what the maximum number of
* attempts for that message should be.
*/
export interface MessageRetryStrategy {
/**
* Checks if the message should be retried after an error occurred. The number
* of processing attempts (including the current) is available in the message
* object.
* @param message The stored message
* @param error The error that was thrown from the message handler or error handler
* @param source If the error was thrown from the message handler, from the error handler, or from the code handling errors in the error handler.
* @returns true if the message should be retried, otherwise false.
*/
(
message: StoredTransactionalMessage,
error: ExtendedError,
source: 'message-handler' | 'error-handler' | 'error-handler-error',
): boolean;
}
/**
* Get the default message retry strategy. This strategy checks that the maximum
* of finished attempts is not exceeded. The number can be defined in the
* `config.settings.maxAttempts` variable. If the error is a PostgreSQL
* serialization error it will be retried up to 100 times (or up to maxAttempts
* whatever is larger). If another error was thrown from the code handling
* errors in the error handler the message is not retired.
*/
export const defaultMessageRetryStrategy = (
config: FullListenerConfig,
): MessageRetryStrategy => {
return (
message: StoredTransactionalMessage,
error: ExtendedError,
source: 'message-handler' | 'error-handler' | 'error-handler-error',
): boolean => {
if (isPgSerializationError(error.innerError)) {
// retry PostgreSQL serialization/deadlock failures up to 100 attempts
return (
message.startedAttempts <= Math.max(config.settings.maxAttempts, 100)
);
}
if (source === 'error-handler-error') {
// If even the code that handles errors in the error handler throws an error the message is not retried.
return false;
}
return message.finishedAttempts < config.settings.maxAttempts;
};
};