UNPKG

resolve-local-event-broker

Version:

The reSolve framework's event broker for applications on a local machine.

265 lines (241 loc) 8.11 kB
"use strict"; 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