UNPKG

@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
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 {};