@arturwojnar/hermes-postgresql
Version:
Production-Ready TypeScript Outbox Pattern for PostgreSQL
75 lines • 2.4 kB
JavaScript
import { assertNever, Duration } from '@arturwojnar/hermes';
import { setTimeout } from 'node:timers/promises';
const createPublishingQueue = (publish, options) => {
const waitAfterFailedPublish = options?.waitAfterFailedPublish || Duration.ofSeconds(1);
const onFailedPublish = options?.onFailedPublish || (() => Promise.resolve());
const ids = new Set();
const messages = new Array();
let isPublishing = false;
const queue = (messageToPublish) => {
if (ids.has(messageToPublish.transaction.lsn)) {
return;
}
ids.add(messageToPublish.transaction.lsn);
messages.push(messageToPublish);
};
const unqueueOldest = () => {
if (messages.length === 0) {
return;
}
const oldest = messages.shift();
ids.delete(oldest.transaction.lsn);
};
const publishOldestMessage = async () => {
if (messages.length === 0) {
return 'exhausted';
}
const oldest = messages[0];
try {
await publish(oldest);
unqueueOldest();
console.info(`Published ${oldest.transaction.lsn}`);
return 'published';
}
catch (error) {
return 'failed';
}
};
const publishMessages = async () => {
if (isPublishing) {
return;
}
isPublishing = true;
try {
do {
const result = await publishOldestMessage();
switch (result) {
case 'published':
continue;
case 'failed':
await onFailedPublish(messages[0].transaction);
if (waitAfterFailedPublish) {
await setTimeout(waitAfterFailedPublish.ms);
}
break;
case 'exhausted':
isPublishing = false;
break;
default:
assertNever(result);
}
} while (isPublishing);
}
finally {
isPublishing = false;
}
};
const size = () => messages.length;
return {
queue,
publishMessages,
size,
};
};
export { createPublishingQueue };
//# sourceMappingURL=publishingQueue.js.map