Support for Workflow Update (experimental)
-
Introduced Workflow Update APIs, both on Workflow side and Client side (#1277 and #1312). Kudos to @dandavison 🙏.
⚠️ The Workflow Update feature is currently in Public Preview. Related APIs are considered experimental and may change in the near future. This feature requires Temporal Server version 1.21 or later, and must be enabled through configuration.
Here are some examples to get started:
// An Update is defined similarly to Query and Signal. The second type parameter (input) is optional const myUpdate = wf.defineUpdate<string, [string]>('myUpdate'); // A handler is a sync or async function; the optional validator is always sync. wf.setHandler(myUpdate, handler, { validator }); // `await wfHandle.executeUpdate` to start an update and block until it completes or fails. // The signature resembles `WorkflowClient.start` in that the arguments are supplied // in the optional options object // (as opposed to the variadic signatures of wfHandle.query and wfHandle.signal) // An error is thrown if the RPC times out (we do not poll for this call) // or if the validator rejects, or the handler throws ApplicationFailure. const updateResult = await handle.executeUpdate(myUpdate, { args: ['a'] }); // `startUpdate` to block until Accepted and obtain a handle to the Update. // Signature the same as `executeUpdate`. const updateHandle = await handle.startUpdate(myUpdate, { args: ['a'] }); // Blocking call to fetch result, with polling. // Validation failures or ApplicationFailure in the handler would throw here. const updateResult = await updateHandle.result();
Major overhaul of logging support
This release feature a major overhaul of Workflow logging, Activity logging support, and Core log forwarding support.
These changes add up to better DX, notably by promoting clear usage patterns that work out of the box, remove the need to use insufficiently documented features, and remove some requirements that were error-prone and recurring source of support questions.
Workflow Logging
-
Calling
appendDefaultInterceptors()when registering custom interceptors is no longer required in order for Workflow logger support to work properly.appendDefaultInterceptors()is now deprecated and behave as a no-op in most cases (i.e. except when a custom logger is supplied in order to redirect log emitted by activities) (#1290). -
Similarly, calling
defaultSink()when registering custom sinks is no longer required for Workflow logging to work properly (#1283). SDK's logger sink is now forcibly injected. Registering a custom logger sink nameddefaultWorkerLoggeris still supported, but discouraged and deprecated. -
Attributes from the current Workflow are now automatically included as metadata on every log entries emited using the Workflow context logger, and some key events of the Workflow's lifecycle are now automatically logged (at
DEBUGlevel for most messages;WARNfor failures) (#1290). -
WorkflowLogInterceptorhas been deprecated (#1290).
Activity Logging
-
Attributes from the current Activity context are now automatically included as metadata on every log entries emited using the Activity context logger, and some key events of the Activity's lifecycle are now automatically logged (at
DEBUGlevel for most messages;WARNfor failures) (#1284). -
Logged Activity attributes can be customized by registering an
ActivityOutboundCallsInterceptorthat intercepts thegetLogAttributes()method (#1284). -
ActivityInboundLogInterceptorhas been deprecated; registering such an interceptor with a custom logger still works, but will prevent the use of the newly introducedgetLogAttributes()interceptor. The same is true about modifying the context logger directly (eg.context.log = myCustomLogger) (#1284).
Core Logging
-
Log entries forwarded from Core to TS now retain metadata (#1225).
-
Reduce the default Core log level to
WARNand non Core Rust logs toERROR(#1270).
Improvements to Activity-related APIs
-
💥 Remote activities may now receive cancellation for schedule-to-close and start-to-close timeouts, without heartbeating. Heartbeating is still required to receive server sent cancellations.
-
All APIs provided through
Context.current()can now be accessed directly from the@temporalio/activityimport (#1252). Also, thanks to @SergeiMeza for fixing a related issue that would occur whenheartbeatandsleepfunctions were destructured out ofContext.current()(#1249) 🙏.Instead of this:
import { Context } from `@temporalio/activity`; // ... Context.current().sleep('5 minutes');you can now do:
import { sleep } from `@temporalio/activity`; // ... sleep('5 minutes'); -
ActivityInboundCallsInterceptorFactoryhas been deprecated. Activity Interceptors should now be instantiated using anActivityInterceptorsFactory(#1284). -
MockActivityEnvironmentnow accept a list of activity interceptors, and more closely match an actual activity execution (#1284).
Improvements to Client-related APIs
-
Allow specifying gRPC
CallCredentialson Client connection. Also, fixed the fact thatConnectionOptions.credentialswas completely ignored (#1261). -
ClientandConnectionclasses now expose awithAbortSignal()method. This can be used to cancel all ongoing connections started within a function's scope. (#1272)Example:
const ctrl = new AbortController(); setTimeout(() => ctrl.abort(), 10_000); // 👇 throws if incomplete by the timeout. await conn.withAbortSignal(ctrl.signal, () => { /* make client or direct grpc calls */); -
Add
startDelayto workflow (signal) start options (#1300)
Miscellaneous
-
WorkerOptionsnow expose anonStickyToStickyPollRatioproperty, which control the proportion of Workflow Task Pollers assigned to polling from Regular Task Queue vs to polling from Sticky Tasks Queues. (#1254) -
Enable
reuseV8Contextby default (#1310) -
Fixed a bug that could happen if the
@temporalio/protowas minimized (#1279)