@message-queue-toolkit/core
Version:
Useful utilities, interfaces and base classes for message queue handling. Supports AMQP and SQS with a common abstraction on top currently
132 lines (131 loc) • 9.69 kB
TypeScript
import { type CommonLogger, type Either, type ErrorReporter, type ErrorResolver } from '@lokalise/node-core';
import type { ZodSchema, ZodType } from 'zod/v3';
import type { MessageInvalidFormatError, MessageValidationError } from '../errors/Errors.ts';
import { AcquireLockTimeoutError, type DeduplicationRequester, type MessageDeduplicationConfig, type ReleasableLock } from '../message-deduplication/messageDeduplicationTypes.ts';
import { type OffloadedPayloadPointerPayload } from '../payload-store/offloadedPayloadMessageSchemas.ts';
import type { PayloadStoreConfig } from '../payload-store/payloadStoreTypes.ts';
import type { MessageProcessingResult } from '../types/MessageQueueTypes.ts';
import type { DeletionConfig, MessageMetricsManager, QueueDependencies, QueueOptions } from '../types/queueOptionsTypes.ts';
import type { BarrierCallback, BarrierResult, MessageHandlerConfig, PreHandlingOutputs, Prehandler, PrehandlerResult } from './HandlerContainer.ts';
import type { HandlerSpy, PublicHandlerSpy } from './HandlerSpy.ts';
import { MessageSchemaContainer } from './MessageSchemaContainer.ts';
export type Deserializer<MessagePayloadType extends object> = (message: unknown, type: ZodType<MessagePayloadType>, errorProcessor: ErrorResolver) => Either<MessageInvalidFormatError | MessageValidationError, MessagePayloadType>;
type CommonQueueLocator = {
queueName: string;
};
export type ResolvedMessage = {
body: unknown;
attributes?: Record<string, unknown>;
};
export declare abstract class AbstractQueueService<MessagePayloadSchemas extends object, MessageEnvelopeType extends object, DependenciesType extends QueueDependencies, QueueConfiguration extends object, QueueLocatorType extends object = CommonQueueLocator, OptionsType extends QueueOptions<QueueConfiguration, QueueLocatorType> = QueueOptions<QueueConfiguration, QueueLocatorType>, ExecutionContext = undefined, PrehandlerOutput = undefined> {
/**
* Used to keep track of the number of `retryLater` results received for a message to be able to
* calculate the delay for the next retry
*/
private readonly messageRetryLaterCountField;
/**
* Used to know when the message was sent initially so we can have a max retry date and avoid
* a infinite `retryLater` loop
*/
protected readonly messageTimestampField: string;
/**
* Used to know the message deduplication id
*/
protected readonly messageDeduplicationIdField: string;
/**
* Used to know the store-based message deduplication options
*/
protected readonly messageDeduplicationOptionsField: string;
protected readonly errorReporter: ErrorReporter;
readonly logger: CommonLogger;
protected readonly messageIdField: string;
protected readonly messageTypeField: string;
protected readonly logMessages: boolean;
protected readonly creationConfig?: QueueConfiguration;
protected readonly locatorConfig?: QueueLocatorType;
protected readonly deletionConfig?: DeletionConfig;
protected readonly payloadStoreConfig?: Omit<PayloadStoreConfig, 'serializer'> & Required<Pick<PayloadStoreConfig, 'serializer'>>;
protected readonly messageDeduplicationConfig?: MessageDeduplicationConfig;
protected readonly messageMetricsManager?: MessageMetricsManager<MessagePayloadSchemas>;
protected readonly _handlerSpy?: HandlerSpy<MessagePayloadSchemas>;
protected isInitted: boolean;
get handlerSpy(): PublicHandlerSpy<MessagePayloadSchemas>;
constructor({ errorReporter, logger, messageMetricsManager }: DependenciesType, options: OptionsType);
protected resolveConsumerMessageSchemaContainer(options: {
handlers: MessageHandlerConfig<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>[];
messageTypeField: string;
}): MessageSchemaContainer<MessagePayloadSchemas>;
protected resolvePublisherMessageSchemaContainer(options: {
messageSchemas: readonly ZodSchema<MessagePayloadSchemas>[];
messageTypeField: string;
}): MessageSchemaContainer<MessagePayloadSchemas>;
protected abstract resolveSchema(message: MessagePayloadSchemas): Either<Error, ZodSchema<MessagePayloadSchemas>>;
protected abstract resolveMessage(message: MessageEnvelopeType): Either<MessageInvalidFormatError | MessageValidationError, ResolvedMessage>;
/**
* Format message for logging
*/
protected resolveMessageLog(message: MessagePayloadSchemas, _messageType: string): unknown;
/**
* Log preformatted and potentially presanitized message payload
*/
protected logMessage(messageLogEntry: unknown): void;
protected handleError(err: unknown, context?: Record<string, unknown>): void;
protected handleMessageProcessed(params: {
message: MessagePayloadSchemas | null;
processingResult: MessageProcessingResult;
messageProcessingStartTimestamp: number;
queueName: string;
messageId?: string;
}): void;
private resolveProcessedMessageMetadata;
protected processPrehandlersInternal(preHandlers: Prehandler<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>[], message: MessagePayloadSchemas): Promise<PrehandlerOutput>;
protected preHandlerBarrierInternal<BarrierOutput>(barrier: BarrierCallback<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput, BarrierOutput> | undefined, message: MessagePayloadSchemas, executionContext: ExecutionContext, preHandlerOutput: PrehandlerOutput): Promise<BarrierResult<BarrierOutput>>;
shouldBeRetried(message: MessagePayloadSchemas, maxRetryDuration: number): boolean;
protected getMessageRetryDelayInSeconds(message: MessagePayloadSchemas): number;
protected updateInternalProperties(message: MessagePayloadSchemas): MessagePayloadSchemas;
private tryToExtractTimestamp;
private tryToExtractNumberOfRetries;
protected abstract resolveNextFunction(preHandlers: Prehandler<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>[], message: MessagePayloadSchemas, index: number, preHandlerOutput: PrehandlerOutput, resolve: (value: PrehandlerOutput | PromiseLike<PrehandlerOutput>) => void, reject: (err: Error) => void): (preHandlerResult: PrehandlerResult) => void;
protected resolveNextPreHandlerFunctionInternal(preHandlers: Prehandler<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>[], executionContext: ExecutionContext, message: MessagePayloadSchemas, index: number, preHandlerOutput: PrehandlerOutput, resolve: (value: PrehandlerOutput | PromiseLike<PrehandlerOutput>) => void, reject: (err: Error) => void): (preHandlerResult: PrehandlerResult) => void;
protected abstract processPrehandlers(message: MessagePayloadSchemas, messageType: string): Promise<PrehandlerOutput>;
protected abstract preHandlerBarrier<BarrierOutput>(message: MessagePayloadSchemas, messageType: string, preHandlerOutput: PrehandlerOutput): Promise<BarrierResult<BarrierOutput>>;
protected abstract processMessage(message: MessagePayloadSchemas, messageType: string, preHandlingOutputs: PreHandlingOutputs<PrehandlerOutput, any>): Promise<Either<'retryLater', 'success'>>;
abstract close(): Promise<unknown>;
/**
* Offload message payload to an external store if it exceeds the threshold.
* Returns a special type that contains a pointer to the offloaded payload or the original payload if it was not offloaded.
* Requires message size as only the implementation knows how to calculate it.
*/
protected offloadMessagePayloadIfNeeded(message: MessagePayloadSchemas, messageSizeFn: () => number): Promise<MessagePayloadSchemas | OffloadedPayloadPointerPayload>;
/**
* Retrieve previously offloaded message payload using provided pointer payload.
* Returns the original payload or an error if the payload was not found or could not be parsed.
*/
protected retrieveOffloadedMessagePayload(maybeOffloadedPayloadPointerPayload: unknown): Promise<Either<Error, unknown>>;
/**
* Checks if the message is duplicated against the deduplication store.
* Returns true if the message is duplicated.
* Returns false if message is not duplicated or deduplication config is missing.
*/
protected isMessageDuplicated(message: MessagePayloadSchemas, requester: DeduplicationRequester): Promise<boolean>;
/**
* Checks if the message is duplicated.
* If it's not, stores the deduplication key in the deduplication store and returns false.
* If it is, returns true.
* If deduplication config is not provided, always returns false to allow further processing of the message.
*/
protected deduplicateMessage(message: MessagePayloadSchemas, requester: DeduplicationRequester): Promise<{
isDuplicated: boolean;
}>;
/**
* Acquires exclusive lock for the message to prevent concurrent processing.
* If lock was acquired successfully, returns a lock object that should be released after processing.
* If lock couldn't be acquired due to timeout (meaning another process acquired it earlier), returns AcquireLockTimeoutError
* If lock couldn't be acquired for any other reasons or if deduplication config is not provided, always returns a lock object that does nothing, so message processing can continue.
*/
protected acquireLockForMessage(message: MessagePayloadSchemas): Promise<Either<AcquireLockTimeoutError, ReleasableLock>>;
protected isDeduplicationEnabledForMessage(message: MessagePayloadSchemas): boolean;
protected getMessageDeduplicationId(message: MessagePayloadSchemas): string | undefined;
private getParsedMessageDeduplicationOptions;
}
export {};