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).
76 lines • 4.11 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runScheduledMessageCleanup = exports.runMessageCleanupOnce = void 0;
const error_1 = require("../common/error");
/**
* Deletes messages from the inbox. Please keep in mind that messages should
* stay for some time especially in the inbox to handle message deduplication.
* @param timesInSeconds Delete messages that
* @param client A database client or directly the database pool. The used DB role must have permissions to delete messages. This is by default the case for the listener role.
* @param config The configuration settings that defines the database schema.
* @returns The number of deleted rows
*/
const runMessageCleanupOnce = (client_1, _a) => __awaiter(void 0, [client_1, _a], void 0, function* (client, { settings: { dbSchema, dbTable, messageCleanupProcessedInSec, messageCleanupAbandonedInSec, messageCleanupAllInSec, }, }) {
var _b;
if (!messageCleanupProcessedInSec &&
!messageCleanupAbandonedInSec &&
!messageCleanupAllInSec) {
return 0;
}
let sql = /* sql */ `DELETE FROM ${dbSchema}.${dbTable} WHERE false`;
const insertValues = [];
if (messageCleanupProcessedInSec) {
insertValues.push(messageCleanupProcessedInSec);
sql += /* sql */ ` OR processed_at < NOW() - ($${insertValues.length} || ' SECOND')::INTERVAL`;
}
if (messageCleanupAbandonedInSec) {
insertValues.push(messageCleanupAbandonedInSec);
sql += /* sql */ ` OR abandoned_at < NOW() - ($${insertValues.length} || ' SECOND')::INTERVAL`;
}
if (messageCleanupAllInSec) {
insertValues.push(messageCleanupAllInSec);
sql += /* sql */ ` OR created_at < NOW() - ($${insertValues.length} || ' SECOND')::INTERVAL`;
}
const rows = yield client.query(`${sql} RETURNING id;`, insertValues);
return (_b = rows.rowCount) !== null && _b !== void 0 ? _b : 0;
});
exports.runMessageCleanupOnce = runMessageCleanupOnce;
/**
*
* @param pool The database pool that should be used to run the cleanup queries. The used DB role must have permissions to delete messages. This is by default the case for the listener role.
* @param config The configuration settings that defines the database schema.
* @param logger A logger object used to log processing issues.
* @returns A timeout from setInterval that you must call once the cleanup should be stopped.
*/
const runScheduledMessageCleanup = (pool, config, logger) => {
const { settings } = config;
if (settings.messageCleanupIntervalInMs) {
return setInterval(() => {
(0, exports.runMessageCleanupOnce)(pool, config)
.then((deleted) => {
if (deleted > 0) {
logger.info(`Deleted ${deleted} ${config.outboxOrInbox} messages during cleanup.`);
}
else {
logger.trace(`Deleted no ${config.outboxOrInbox} messages during cleanup.`);
}
})
.catch((e) => {
const err = (0, error_1.ensureExtendedError)(e, 'MESSAGE_CLEANUP_ERROR');
logger.warn(err, 'Could not run the message cleanup logic.');
});
}, settings.messageCleanupIntervalInMs);
}
return undefined;
};
exports.runScheduledMessageCleanup = runScheduledMessageCleanup;
//# sourceMappingURL=message-cleanup.js.map