UNPKG

@temporalio/worker

Version:
189 lines 8.89 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("@temporalio/common/lib/logger"); const metrics_1 = require("@temporalio/common/lib/metrics"); const UNINITIALIZED = Symbol('UNINITIALIZED'); class Activity { info; fn; dataConverter; heartbeatCallback; cancelReason; context; cancel = () => undefined; abortController = new AbortController(); interceptors; /** * Logger bound to `sdkComponent: worker`, with metadata from this activity. * This is the logger to use for all log messages emitted by the activity * worker. Note this is not exactly the same thing as the activity context * logger, which is bound to `sdkComponent: activity`. */ workerLogger; /** * Metric Meter with tags from this activity, including tags from interceptors. */ metricMeter; constructor(info, fn, dataConverter, heartbeatCallback, workerLogger, workerMetricMeter, interceptors) { this.info = info; this.fn = fn; this.dataConverter = dataConverter; this.heartbeatCallback = heartbeatCallback; this.workerLogger = logger_1.LoggerWithComposedMetadata.compose(workerLogger, this.getLogAttributes.bind(this)); this.metricMeter = metrics_1.MetricMeterWithComposedTags.compose(workerMetricMeter, this.getMetricTags.bind(this)); 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, to be used exclusively from user code logger_1.LoggerWithComposedMetadata.compose(this.workerLogger, { sdkComponent: common_1.SdkComponent.activity }), this.metricMeter); // 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); } getMetricTags() { const baseTags = { namespace: this.info.workflowNamespace, taskQueue: this.info.taskQueue, activityType: this.info.activityType, }; // In case some interceptors use the metric meter while initializing... if (this.interceptors == null) return baseTags; return (0, interceptors_1.composeInterceptors)(this.interceptors.outbound, 'getMetricTags', (a) => a)(baseTags); } /** * 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 { if (error instanceof common_1.ApplicationFailure && error.category === common_1.ApplicationFailureCategory.BENIGN) { // Downgrade log level to DEBUG for benign application errors. this.workerLogger.debug('Activity failed', { error, 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