UNPKG

resolve-local-event-broker

Version:

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

207 lines (183 loc) 7.54 kB
import { NOTIFICATIONS_TABLE_NAME, SUBSCRIBERS_TABLE_NAME, BATCHES_TABLE_NAME, DeliveryStrategy, NotificationStatus, ConsumerMethod, LazinessStrategy, PrivateOperationType } from '../constants'; const retryRollback = new Error('Retrying rollback marker'); async function requestTimeout(pool, payload) { const { database: { runQuery, runRawQuery, escapeStr, escapeId }, parseSubscription, invokeConsumer, invokeOperation, getNextCursor, serializeError } = pool; const { batchId } = payload; const notificationsTableNameAsId = escapeId(NOTIFICATIONS_TABLE_NAME); const subscribersTableNameAsId = escapeId(SUBSCRIBERS_TABLE_NAME); const batchesTableNameAsId = escapeId(BATCHES_TABLE_NAME); const affectedNotifications = await runQuery(` SELECT ${subscribersTableNameAsId}."eventSubscriber" AS "eventSubscriber", ${subscribersTableNameAsId}."deliveryStrategy" AS "deliveryStrategy", ${subscribersTableNameAsId}."subscriptionId" AS "subscriptionId", ${subscribersTableNameAsId}."successEvent" AS "successEvent", ${subscribersTableNameAsId}."cursor" AS "cursor", ${notificationsTableNameAsId}."status" AS "runStatus", ${notificationsTableNameAsId}."xaTransactionId" AS "xaTransactionId", ${notificationsTableNameAsId}."batchId" AS "batchId" FROM ${notificationsTableNameAsId} LEFT JOIN ${subscribersTableNameAsId} ON ${subscribersTableNameAsId}."subscriptionId" = ${notificationsTableNameAsId}."subscriptionId" WHERE ${notificationsTableNameAsId}."batchId" = ${escapeStr(batchId)} LIMIT 1 `); if (affectedNotifications == null || affectedNotifications.length === 0) { return; } const subscriptionDescription = parseSubscription(affectedNotifications[0]); if (subscriptionDescription.runStatus !== NotificationStatus.PROCESSING && subscriptionDescription.runStatus !== NotificationStatus.TIMEOUT_ENTERING && !subscriptionDescription.runStatus.startsWith(NotificationStatus.TIMEOUT_XA_COMMITING) && subscriptionDescription.runStatus !== NotificationStatus.TIMEOUT_XA_ROLLBACKING) { return; } if (subscriptionDescription.runStatus === NotificationStatus.PROCESSING) { await runRawQuery(` UPDATE ${notificationsTableNameAsId} SET "status" = ${escapeStr(NotificationStatus.TIMEOUT_ENTERING)} WHERE ${notificationsTableNameAsId}."batchId" = ${escapeStr(batchId)} AND ${notificationsTableNameAsId}."status" = ${escapeStr(NotificationStatus.PROCESSING)}; COMMIT; BEGIN IMMEDIATE; `); const result = await runQuery(` SELECT ${notificationsTableNameAsId}."status" AS "runStatus" FROM ${notificationsTableNameAsId} WHERE ${notificationsTableNameAsId}."batchId" = ${escapeStr(batchId)} AND ${notificationsTableNameAsId}."status" = ${escapeStr(NotificationStatus.TIMEOUT_ENTERING)} LIMIT 1 `); if (result == null || result.length === 0) { return; } subscriptionDescription.runStatus = NotificationStatus.TIMEOUT_ENTERING; } const { eventSubscriber, deliveryStrategy, runStatus, xaTransactionId, cursor, successEvent: prevSuccessEvent } = subscriptionDescription; const activeBatch = { batchId: subscriptionDescription.batchId, subscriptionId: subscriptionDescription.subscriptionId, eventSubscriber: subscriptionDescription.eventSubscriber }; if (deliveryStrategy === DeliveryStrategy.ACTIVE_XA && xaTransactionId == null || deliveryStrategy !== DeliveryStrategy.ACTIVE_XA) { const input = { type: PrivateOperationType.FINALIZE_BATCH, payload: { activeBatch, result: { error: serializeError(new Error(`Timeout with consuming batch`)) } } }; await invokeOperation(pool, LazinessStrategy.EAGER, input); return; } const result = { successEvent: null, error: null, cursor }; try { if (runStatus === NotificationStatus.TIMEOUT_ENTERING || runStatus.startsWith(NotificationStatus.TIMEOUT_XA_COMMITING)) { let appliedEventCount = null; if (runStatus === NotificationStatus.TIMEOUT_ENTERING) { // eslint-disable-next-line no-bitwise appliedEventCount = ~~(await invokeConsumer(pool, ConsumerMethod.CommitXATransaction, { eventSubscriber, xaTransactionId, batchId, countEvents: true })); await runRawQuery(` UPDATE ${notificationsTableNameAsId} SET "status" = ${escapeStr( // eslint-disable-next-line no-bitwise `${NotificationStatus.TIMEOUT_XA_COMMITING}${~~appliedEventCount}`)} WHERE "batchId" = ${escapeStr(batchId)}; COMMIT; BEGIN IMMEDIATE; `); } else { // eslint-disable-next-line no-bitwise appliedEventCount = ~~runStatus.substring(NotificationStatus.TIMEOUT_XA_COMMITING.length); } if (appliedEventCount > 0) { const applyingEvents = await runQuery(` SELECT * FROM ${batchesTableNameAsId} WHERE ${batchesTableNameAsId}."batchId" = ${escapeStr(batchId)} ORDER BY ${batchesTableNameAsId}."eventIndex" ASC LIMIT ${+appliedEventCount} `); result.successEvent = applyingEvents[appliedEventCount - 1]; const lastSuccessEventIdx = result.successEvent != null ? applyingEvents.findIndex(({ aggregateIdAndVersion }) => aggregateIdAndVersion === `${result.successEvent.aggregateId}:${result.successEvent.aggregateVersion}`) + 1 : 0; result.cursor = await getNextCursor(cursor, applyingEvents.slice(0, lastSuccessEventIdx)); } const isXaCommitOk = await invokeConsumer(pool, ConsumerMethod.CommitXATransaction, { eventSubscriber, xaTransactionId, batchId }); if (!isXaCommitOk) { result.successEvent = prevSuccessEvent; } } else if (runStatus === NotificationStatus.TIMEOUT_XA_ROLLBACKING) { throw retryRollback; } else { throw new Error(`Inconsistent XA-state ${subscriptionDescription.runStatus}`); } } catch (error) { let compositeError = new Error(error.message); compositeError.stack = error.stack; if (runStatus === NotificationStatus.TIMEOUT_ENTERING || runStatus.startsWith(NotificationStatus.TIMEOUT_XA_COMMITING)) { await runRawQuery(` UPDATE ${notificationsTableNameAsId} SET "status" = ${escapeStr(NotificationStatus.TIMEOUT_XA_ROLLBACKING)} WHERE "batchId" = ${escapeStr(batchId)}; COMMIT; BEGIN IMMEDIATE; `); } try { const isXaRollbackOk = await invokeConsumer(pool, ConsumerMethod.RollbackXATransaction, { eventSubscriber, xaTransactionId, batchId }); if (!isXaRollbackOk) { throw new Error(`Xa-transaction ${xaTransactionId} early marked to rollback, but was auto-committed`); } } catch (rollbackError) { compositeError.message = `${compositeError.message}\n${rollbackError.message}`; compositeError.stack = `${compositeError.stack}\n${rollbackError.stack}`; } result.error = serializeError(compositeError); } const input = { type: PrivateOperationType.FINALIZE_BATCH, payload: { activeBatch, result } }; await invokeOperation(pool, LazinessStrategy.EAGER, input); } export default requestTimeout; //# sourceMappingURL=request-timeout.js.map