resolve-local-event-broker
Version:
The reSolve framework's event broker for applications on a local machine.
265 lines (241 loc) • 8.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _constants = require("../constants");
const deliverBatchForSubscriber = async (pool, payload) => {
const {
database: {
runRawQuery,
runQuery,
escapeStr,
escapeId,
decodeJsonPath
},
parseSubscription,
invokeConsumer,
invokeOperation,
serializeError,
checkCursorEdge
} = pool;
const {
activeBatch
} = payload;
const {
batchId,
subscriptionId,
eventSubscriber
} = activeBatch;
const subscribersTableNameAsId = escapeId(_constants.SUBSCRIBERS_TABLE_NAME);
const notificationsTableNameAsId = escapeId(_constants.NOTIFICATIONS_TABLE_NAME);
const batchesTableNameAsId = escapeId(_constants.BATCHES_TABLE_NAME);
const result = await runQuery(`
SELECT ${subscribersTableNameAsId}."eventTypes" AS "eventTypes",
${subscribersTableNameAsId}."aggregateIds" AS "aggregateIds",
${subscribersTableNameAsId}."status" AS "status",
${subscribersTableNameAsId}."deliveryStrategy" AS "deliveryStrategy",
${subscribersTableNameAsId}."queueStrategy" AS "queueStrategy",
${subscribersTableNameAsId}."properties" AS "properties",
${subscribersTableNameAsId}."cursor" AS "cursor",
${notificationsTableNameAsId}."status" AS "runStatus",
${notificationsTableNameAsId}."xaTransactionId" AS "xaTransactionId",
(${subscribersTableNameAsId}."successEvent" IS NOT NULL OR
${subscribersTableNameAsId}."failedEvent" IS NOT NULL) AS "isEventBasedRun",
${subscribersTableNameAsId}."status" AS "status",
${subscribersTableNameAsId}."errors" IS NOT NULL AS "hasErrors"
FROM ${notificationsTableNameAsId} LEFT JOIN ${subscribersTableNameAsId}
ON ${subscribersTableNameAsId}."subscriptionId" =
${notificationsTableNameAsId}."subscriptionId"
WHERE ${subscribersTableNameAsId}."subscriptionId" = ${escapeStr(subscriptionId)}
LIMIT 1
`);
if (result == null || result.length !== 1) {
throw new Error('Cannot retrieve subscriber information');
}
const {
eventTypes,
aggregateIds,
deliveryStrategy,
cursor,
runStatus,
xaTransactionId: existingXaTransactionId,
properties,
isEventBasedRun,
status,
hasErrors
} = await parseSubscription(result[0]);
if (status === _constants.SubscriptionStatus.ERROR || hasErrors) {
const input = {
type: _constants.PrivateOperationType.FINALIZE_BATCH,
payload: {
activeBatch,
result: null
}
};
await invokeOperation(pool, _constants.LazinessStrategy.EAGER, input);
return;
}
if (deliveryStrategy !== _constants.DeliveryStrategy.ACTIVE_NONE && deliveryStrategy !== _constants.DeliveryStrategy.ACTIVE_REGULAR && deliveryStrategy !== _constants.DeliveryStrategy.ACTIVE_XA) {
throw new Error(`Wrong deliveryStrategy="${deliveryStrategy}"`);
}
let [events, xaTransactionId] = [null, existingXaTransactionId];
if (isEventBasedRun) {
try {
void ({
events
} = await invokeConsumer(pool, _constants.ConsumerMethod.LoadEvents, {
eventTypes,
aggregateIds,
limit: 500,
cursor
}));
if (events == null || events.length === 0) {
const input = {
type: _constants.PrivateOperationType.FINALIZE_BATCH,
payload: {
activeBatch,
result: null
}
};
await invokeOperation(pool, _constants.LazinessStrategy.EAGER, input);
return;
}
if (!checkCursorEdge(events, cursor)) {
const input = {
type: _constants.PrivateOperationType.FINALIZE_BATCH,
payload: {
activeBatch,
result: {
error: serializeError(new Error(`Events batch ${batchId} has conflicted with its cursor`))
}
}
};
await invokeOperation(pool, _constants.LazinessStrategy.EAGER, input);
return;
}
if (deliveryStrategy === _constants.DeliveryStrategy.ACTIVE_XA) {
if (xaTransactionId == null) {
xaTransactionId = await invokeConsumer(pool, _constants.ConsumerMethod.BeginXATransaction, {
eventSubscriber,
batchId
});
}
if (xaTransactionId == null) {
throw new Error(`Failed to start XA session`);
}
}
} catch (error) {
const input = {
type: _constants.PrivateOperationType.FINALIZE_BATCH,
payload: {
activeBatch,
result: {
error: serializeError(error)
}
}
};
await invokeOperation(pool, _constants.LazinessStrategy.EAGER, input);
return;
}
if (runStatus === _constants.NotificationStatus.RECIEVED) {
await runRawQuery(`
INSERT INTO ${batchesTableNameAsId}(
"batchId",
"eventIndex",
"threadId",
"threadCounter",
"aggregateIdAndVersion"
) VALUES ${events.map(({
threadId,
threadCounter,
aggregateId,
aggregateVersion
}, idx) => `(${escapeStr(batchId)},${+idx},${+threadId},${+threadCounter},${escapeStr(`${aggregateId}:${aggregateVersion}`)})`).join(', ')};
UPDATE ${notificationsTableNameAsId} SET "xaTransactionId" =
json(${escapeStr(JSON.stringify(xaTransactionId))}),
"status" = ${escapeStr(_constants.NotificationStatus.PROCESSING)}
WHERE "batchId" = ${escapeStr(batchId)};
COMMIT;
BEGIN IMMEDIATE;
`);
} else {
await runRawQuery(`
DELETE FROM ${batchesTableNameAsId} WHERE
${batchesTableNameAsId}."batchId" = ${escapeStr(batchId)};
INSERT INTO ${batchesTableNameAsId}(
"batchId",
"eventIndex",
"threadId",
"threadCounter",
"aggregateIdAndVersion"
) VALUES ${events.map(({
threadId,
threadCounter,
aggregateId,
aggregateVersion
}, idx) => `(${escapeStr(batchId)},${+idx},${+threadId},${+threadCounter},${escapeStr(`${aggregateId}:${aggregateVersion}`)})`).join(', ')};
COMMIT;
BEGIN IMMEDIATE;
`);
}
} else {
// TODO: improve Initial event timestamp for sagas
await runRawQuery(`
UPDATE ${notificationsTableNameAsId} SET
"status" = ${escapeStr(_constants.NotificationStatus.PROCESSING)}
WHERE "batchId" = ${escapeStr(batchId)};
COMMIT;
BEGIN IMMEDIATE;
`);
let initialEvent = null;
try {
const initialEvents = (await invokeConsumer(pool, _constants.ConsumerMethod.LoadEvents, {
eventTypes,
aggregateIds,
limit: 1,
cursor: null
})).events;
if (initialEvents != null && initialEvents.length > 0) {
void ([initialEvent] = initialEvents);
}
} catch (error) {
const input = {
type: _constants.PrivateOperationType.FINALIZE_BATCH,
payload: {
activeBatch,
result: {
error: serializeError(error)
}
}
};
await invokeOperation(pool, _constants.LazinessStrategy.EAGER, input);
return;
}
events = [{
timestamp: initialEvent != null ? initialEvent.timestamp : 0,
type: 'Init'
}];
}
const input = {
type: _constants.PrivateOperationType.REQUEST_TIMEOUT,
payload: {
batchId
}
};
await invokeOperation(pool, _constants.LazinessStrategy.LAZY, input, _constants.BATCH_CONSUMING_TIME);
const sendingProperties = properties != null ? Object.keys(properties).reduce((acc, key) => {
acc[decodeJsonPath(key)] = properties[key];
return acc;
}, {}) : {};
await invokeConsumer(pool, _constants.ConsumerMethod.SendEvents, {
xaTransactionId,
eventSubscriber,
properties: sendingProperties,
events,
batchId
}, true);
};
var _default = deliverBatchForSubscriber;
exports.default = _default;
//# sourceMappingURL=deliver-batch-for-subscriber.js.map