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).
79 lines • 3.87 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.initializeMessageStorage = void 0;
const error_1 = require("../common/error");
/**
* Initialize the message storage to store outbox or inbox messages in the corresponding table.
* @param config The configuration object that defines the values on how to connect to the database and general settings.
* @param logger A logger instance for logging trace up to error logs
* @returns Initializes the function to store the outbox or inbox message data to the database and provides the shutdown action.
*/
const initializeMessageStorage = ({ settings, outboxOrInbox, }, logger) => {
/**
* The function to store the message data to the database.
* @param message The received message that should be stored as a outbox or inbox message
* @param client A database client with an active transaction(!) can be provided. Otherwise
* @throws Error if the message could not be stored
*/
return (message, client) => __awaiter(void 0, void 0, void 0, function* () {
try {
yield insertMessage(message, client, settings, logger);
}
catch (err) {
const messageError = new error_1.MessageError(`Could not store the ${outboxOrInbox} message with id ${message.id}`, 'MESSAGE_STORAGE_FAILED', message, err);
logger.error(messageError, `Could not store the ${outboxOrInbox} message`);
throw messageError;
}
});
};
exports.initializeMessageStorage = initializeMessageStorage;
const insertMessage = (message_1, client_1, _a, logger_1) => __awaiter(void 0, [message_1, client_1, _a, logger_1], void 0, function* (message, client, { dbSchema, dbTable }, logger) {
const { id, aggregateType, aggregateId, messageType, segment, payload, metadata, concurrency, createdAt, lockedUntil, } = message;
const insertValues = [
id,
aggregateType,
aggregateId,
messageType,
segment,
payload,
metadata,
];
let optionalPlaceholders = '';
const addPlaceholder = () => (optionalPlaceholders += `, $${insertValues.length}`);
let optionalFields = '';
const addOptionalFields = (name) => (optionalFields += `, ${name}`);
if (concurrency) {
insertValues.push(concurrency);
addOptionalFields('concurrency');
addPlaceholder();
}
if (createdAt) {
insertValues.push(createdAt);
addOptionalFields('created_at');
addPlaceholder();
}
if (lockedUntil) {
insertValues.push(lockedUntil);
addOptionalFields('locked_until');
addPlaceholder();
}
const messageResult = yield client.query(
/* sql */ `
INSERT INTO ${dbSchema}.${dbTable}
(id, aggregate_type, aggregate_id, message_type, segment, payload, metadata${optionalFields})
VALUES ($1, $2, $3, $4, $5, $6, $7${optionalPlaceholders})
ON CONFLICT (id) DO NOTHING`, insertValues);
if (!messageResult.rowCount || messageResult.rowCount < 1) {
logger.warn(message, `The message with id ${id} already existed`);
}
});
//# sourceMappingURL=initialize-message-storage.js.map