UNPKG

@temporalio/worker

Version:
152 lines 7.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Activity = void 0; exports.activityLogAttributes = activityLogAttributes; require("abort-controller/polyfill"); // eslint-disable-line import/no-unassigned-import const activity_1 = require("@temporalio/activity"); const common_1 = require("@temporalio/common"); const internal_non_workflow_1 = require("@temporalio/common/lib/internal-non-workflow"); const interceptors_1 = require("@temporalio/common/lib/interceptors"); const type_helpers_1 = require("@temporalio/common/lib/type-helpers"); const logger_1 = require("./logger"); const UNINITIALIZED = Symbol('UNINITIALIZED'); class Activity { constructor(info, fn, dataConverter, heartbeatCallback, workerLogger, interceptors) { this.info = info; this.fn = fn; this.dataConverter = dataConverter; this.heartbeatCallback = heartbeatCallback; this.cancel = () => undefined; this.abortController = new AbortController(); this.workerLogger = (0, logger_1.withMetadata)(workerLogger, () => this.getLogAttributes()); const promise = new Promise((_, reject) => { this.cancel = (reason) => { this.cancelReason = reason; const err = new common_1.CancelledFailure(reason); this.abortController.abort(err); reject(err); }; }); this.context = new activity_1.Context(info, promise, this.abortController.signal, this.heartbeatCallback, // This is the activity context logger (0, logger_1.withMetadata)(this.workerLogger, { sdkComponent: common_1.SdkComponent.activity })); // Prevent unhandled rejection promise.catch(() => undefined); this.interceptors = { inbound: [], outbound: [] }; interceptors .map((factory) => factory(this.context)) .forEach(({ inbound, outbound }) => { if (inbound) this.interceptors.inbound.push(inbound); if (outbound) this.interceptors.outbound.push(outbound); }); } getLogAttributes() { const logAttributes = activityLogAttributes(this.info); // In case some interceptor uses the logger while initializing... if (this.interceptors == null) return logAttributes; return (0, interceptors_1.composeInterceptors)(this.interceptors.outbound, 'getLogAttributes', (a) => a)(logAttributes); } /** * Actually executes the function. * * Any call up to this function and including this one will be trimmed out of stack traces. */ async execute(fn, input) { let error = UNINITIALIZED; // In case someone decides to throw undefined... const startTime = process.hrtime.bigint(); this.workerLogger.debug('Activity started'); try { const executeNextHandler = ({ args }) => fn(...args); const executeWithInterceptors = (0, interceptors_1.composeInterceptors)(this.interceptors.inbound, 'execute', executeNextHandler); return await executeWithInterceptors(input); } catch (err) { error = err; throw err; } finally { const durationNanos = process.hrtime.bigint() - startTime; const durationMs = Number(durationNanos / 1000000n); if (error === UNINITIALIZED) { this.workerLogger.debug('Activity completed', { durationMs }); } else if ((error instanceof common_1.CancelledFailure || (0, type_helpers_1.isAbortError)(error)) && this.context.cancellationSignal.aborted) { this.workerLogger.debug('Activity completed as cancelled', { durationMs }); } else if (error instanceof activity_1.CompleteAsyncError) { this.workerLogger.debug('Activity will complete asynchronously', { durationMs }); } else { this.workerLogger.warn('Activity failed', { error, durationMs }); } } } run(input) { return activity_1.asyncLocalStorage.run(this.context, async () => { try { if (this.fn === undefined) throw new common_1.IllegalStateError('Activity function is not defined'); const result = await this.execute(this.fn, input); return { completed: { result: await (0, internal_non_workflow_1.encodeToPayload)(this.dataConverter, result) } }; } catch (err) { if (err instanceof activity_1.CompleteAsyncError) { return { willCompleteAsync: {} }; } if (this.cancelReason === 'HEARTBEAT_DETAILS_CONVERSION_FAILED') { // Ignore actual failure, it is likely a CancelledFailure but server // expects activity to only fail with ApplicationFailure return { failed: { failure: await (0, internal_non_workflow_1.encodeErrorToFailure)(this.dataConverter, common_1.ApplicationFailure.retryable(this.cancelReason, 'CancelledFailure')), }, }; } else if (this.cancelReason) { // Either a CancelledFailure that we threw or AbortError from AbortController if (err instanceof common_1.CancelledFailure) { const failure = await (0, internal_non_workflow_1.encodeErrorToFailure)(this.dataConverter, err); failure.stackTrace = undefined; return { cancelled: { failure } }; } else if ((0, type_helpers_1.isAbortError)(err)) { return { cancelled: { failure: { source: common_1.FAILURE_SOURCE, canceledFailureInfo: {} } } }; } } return { failed: { failure: await (0, internal_non_workflow_1.encodeErrorToFailure)(this.dataConverter, (0, common_1.ensureApplicationFailure)(err)), }, }; } }); } runNoEncoding(fn, input) { if (this.fn !== undefined) throw new common_1.IllegalStateError('Activity function is defined'); return activity_1.asyncLocalStorage.run(this.context, () => this.execute(fn, input)); } } exports.Activity = Activity; /** * Returns a map of attributes to be set on log messages for a given Activity */ function activityLogAttributes(info) { return { isLocal: info.isLocal, attempt: info.attempt, namespace: info.workflowNamespace, taskToken: info.base64TaskToken, workflowId: info.workflowExecution.workflowId, workflowRunId: info.workflowExecution.runId, workflowType: info.workflowType, activityId: info.activityId, activityType: info.activityType, taskQueue: info.taskQueue, }; } //# sourceMappingURL=activity.js.map