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).
61 lines (57 loc) • 2.49 kB
text/typescript
import { TransactionalMessage } from '../../message/transactional-message';
import { ReplicationConcurrencyController } from './concurrency-controller';
import { createReplicationFullConcurrencyController } from './create-full-concurrency-controller';
import { createReplicationMutexConcurrencyController } from './create-mutex-concurrency-controller';
import { createReplicationSegmentMutexConcurrencyController } from './create-segment-mutex-concurrency-controller';
import { createReplicationSemaphoreConcurrencyController } from './create-semaphore-concurrency-controller';
export type ReplicationMultiConcurrencyType =
| 'mutex'
| 'semaphore'
| 'full-concurrency'
| 'segment-mutex';
/**
* Use different mutex controllers depending on the desired concurrency level
* for different messages. If a semaphore concurrency controller should be used,
* the `maxSemaphoreParallelism` should be set.
* @param getConcurrencyType Implements the logic which concurrency controller should be used e.g. based on the aggregate and message type
* @returns The controller to acquire and release the mutex
*/
export const createReplicationMultiConcurrencyController = (
getConcurrencyType: (
message: TransactionalMessage,
) => ReplicationMultiConcurrencyType,
settings?: {
maxSemaphoreParallelism?: number;
},
): ReplicationConcurrencyController => {
const fullConcurrencyController =
createReplicationFullConcurrencyController();
const mutexController = createReplicationMutexConcurrencyController();
const segmentMutexController =
createReplicationSegmentMutexConcurrencyController();
const semaphore = createReplicationSemaphoreConcurrencyController(
settings?.maxSemaphoreParallelism ?? 5,
);
return {
/** Acquire a lock (if any) and return a function to release it. */
acquire: (message: TransactionalMessage): Promise<() => void> => {
switch (getConcurrencyType(message)) {
case 'full-concurrency':
return fullConcurrencyController.acquire(message);
case 'mutex':
return mutexController.acquire(message);
case 'semaphore':
return semaphore.acquire(message);
case 'segment-mutex':
return segmentMutexController.acquire(message);
}
},
/** Cancel all controllers. */
cancel: () => {
fullConcurrencyController.cancel();
mutexController.cancel();
segmentMutexController?.cancel();
semaphore.cancel();
},
};
};