Skip to content

Commit 2910a61

Browse files
mjameswhbergundy
andauthored
chore: Update CHANGELOG.md file in prep for 1.8.0 and make last minute API changes (#1148)
## What changed - Updated CHANGELOG.md file in prep for 1.8.0. - [Update core to get local activity start-to-close fix](e920da3) - [Fix flake in test-isolation](7153d4d) - [Rename activity context logger to log, moar changelog edits](8379ca4) - [Add versioning changelog and minor API changes](7999b73) --------- Co-authored-by: Roey Berman <roey.berman@gmail.com>
1 parent 450c713 commit 2910a61

File tree

12 files changed

+212
-44
lines changed

12 files changed

+212
-44
lines changed

CHANGELOG.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,169 @@ All notable changes to this project will be documented in this file.
44

55
Breaking changes marked with a :boom:
66

7+
## [1.8.0] - 2023-06-22
8+
9+
### Features
10+
11+
- [`worker`] Add support for [worker versioning](https://docs.temporal.io/workers#worker-versioning) ([#1146](https://github.com/temporalio/sdk-typescript/pull/1146)).
12+
13+
Worker versioning is available from server version 1.21 (if enabled in dynamic configuration).
14+
15+
:warning: Experimental - While the feature is well tested and is considered functionally stable, the SDK APIs are
16+
considered experimental.
17+
18+
To use worker versioning with the TypeScript SDK, use the following steps:
19+
20+
```ts
21+
import { Client } from '@temporalio/client';
22+
import { Worker } from '@temporalio/worker';
23+
24+
const buildId = 'id-generated-from-continuous-integration';
25+
26+
// Deploy new workers, opt them in to versioning.
27+
const worker = await Worker.create({
28+
workflowsPath: require.resolve('./workflows'),
29+
buildId,
30+
useVersioning: true,
31+
// ...
32+
});
33+
34+
// ...
35+
36+
// In a separate process, when all workers are up, update the build id compatibility for the task queue.
37+
const client = new Client({
38+
/* options */
39+
});
40+
// If the current version is incompatible with the previous ones:
41+
await client.taskQueue.updateBuildIdCompatibility('my-task-queue', {
42+
operation: 'addNewIdInNewDefaultSet',
43+
buildId,
44+
});
45+
// Or, if the current version is compatible with a previous one:
46+
await client.taskQueue.updateBuildIdCompatibility('my-task-queue', {
47+
operation: 'addNewCompatibleVersion',
48+
buildId,
49+
existingCompatibleBuildId: 'some-other-build-id',
50+
});
51+
52+
// Check if workers are reachable before retiring them (even if their build ids are associated with multiple task
53+
// queues):
54+
const { buildIdReachability } = await client.taskQueue.getReachability({ buildIds: ['some-other-build-id'] });
55+
const { taskQueueReachability } = buildIdReachability['some-other-build-id'];
56+
for (const [taskQueue, reachability] of Object.entries(taskQueueReachability)) {
57+
if (reachability.length > 0) {
58+
if (reachability[0] === 'NotFetched') {
59+
// We asked the server for too many reachability entries (build ids or task queues),
60+
// This build id / task queue reachability should be fetched in another request.
61+
// Fetch this information here or later...
62+
} else {
63+
console.log('Build id still reachable on:', taskQueue);
64+
}
65+
}
66+
}
67+
// Check if build id is reachable...
68+
```
69+
70+
- [`worker`] Add support for using multiple concurrent pollers to fetch Workflow Tasks and Activity Tasks from Task Queues
71+
([#1132](https://github.com/temporalio/sdk-typescript/pull/1132)).
72+
73+
The number of pollers for each type can be controlled through the `WorkerOptions.maxConcurrentWorkflowTaskPolls`
74+
and `WorkerOptions.maxConcurrentActivityTaskPolls` properties. Properly adjusting these values should allow better
75+
filling of the corresponding execution slots, which may signficiantly improve a Worker's throughput. Defaults are
76+
10 Workflow Task Pollers and 2 Activity Task Pollers; we however strongly recommend tuning these values
77+
based on workload specific performance tests.
78+
79+
Default value for `maxConcurrentWorkflowTaskExecutions` has also been reduced to 40 (it was previously 100), as recent
80+
performance tests demonstrate that higher values increase the risk of Workflow Task Timeouts unless other options are
81+
also tuned. This was not problem previously because the single poller was unlikely to fill all execution slots, so
82+
maximum would rarely be reached.
83+
84+
- [`workflow`] The `reuseV8Context` worker option is no longer marked as experimental ([#1132](https://github.com/temporalio/sdk-typescript/pull/1132)).
85+
This is a major optimization of the Workflow sandboxing runtime; it allows the worker to reuse a single execution
86+
context across Workflow instances, without compromising the safety of the deterministic sandbox. It significantly
87+
reduces RAM and CPU usage. The formula used to auto-configure `maxCachedWorkflows` has also been reviewed to reflect a
88+
lower memory usage requirement when `reuseV8Context` is enabled.
89+
90+
At this point, you still need to opt-in to this feature by adding `reuseV8Context: true` to your `WorkerOptions`, as
91+
we believe most teams should reconsider their workers's performance settings after enabling this option.
92+
93+
:boom: Note that we are planing enabling this option by default starting with 1.9.0. If for some reason, you prefer to
94+
delay enabling this optimization, then we recommend that you explicitly add `reuseV8Context: false` to your worker
95+
options.
96+
97+
- We now provide out-of-the-box log support from both Workflows and Activity contexts ([#1117](https://github.com/temporalio/sdk-typescript/pull/1117), [#1138](https://github.com/temporalio/sdk-typescript/pull/1138))).
98+
99+
For Workflows, the logger funnels messages through the `defaultWorkerLogger` sink, which itself defaults to forwarding
100+
messages to `Runtime.instance().logger`.
101+
102+
Example usage:
103+
104+
```ts
105+
import * as workflow from '@temporalio/workflow';
106+
107+
export async function myWorkflow(): Promise<void> {
108+
workflow.log.info('hello from my workflow', { key: 'value' });
109+
}
110+
```
111+
112+
For Activities, the logger can be accessed as `Context.logger`. It defaults to `Runtime.instance().logger`, but may be
113+
overriden by interceptors (ie. to set a custom logger). `ActivityInboundLogInterceptor` is still installed by default,
114+
adding enriched metadata from activity context on each log entry.
115+
116+
Example usage:
117+
118+
```ts
119+
import * as activity from '@temporalio/activity';
120+
121+
export async function myActivity(): Promise<void> {
122+
const context = activity.Context.current();
123+
context.log.info('hello from my activity', { key: 'value' });
124+
}
125+
```
126+
127+
- :boom: Protect against 'ms' durations errors ([#1136](https://github.com/temporalio/sdk-typescript/pull/1136)).
128+
There have been several reports of situations where invalid durations resulted in unexpected and hard to diagnose
129+
issues (e.g. can you can predict what `const bool = condition(fn, '1 month')` will do?). We now provide type
130+
definitions for "ms-formated strings" through the newly introduced `Duration` type, which is either a well formed
131+
`ms`-formated string or a number of milliseconds. Invalid ms-formated-strings will also throw at runtime.
132+
133+
Note: this might cause build errors in situations where a non-const string value is passed somewhere we expect a
134+
`Duration`. Consider either validating and converting these strings _before_ passing them as `Duration`, or simply
135+
cast them to `Duration` and deal with runtime exception that might be thrown if an invalid value is provided.
136+
137+
- [`workflow`] Clone sink args at call time on Node 17+
138+
([#1118](https://github.com/temporalio/sdk-typescript/pull/1118)). A subtle aspect of Workflow Sinks is that calls
139+
are actually buffered and get executed only once the current Workflow activation completes. That sometime caused
140+
unexpected behavior where an object passed as argument to a sink function is mutated after the invocation.
141+
142+
On Node.js 17+, we now clone sink arguments at call time, using `structuredClone`. While this adds some runtime
143+
overhead, it leads to more predictable experience, as well as better exceptions when passing non-transferrable objects
144+
to a sink.
145+
146+
- [`core`] Add the `sticky_cache_total_forced_eviction` metric ([Core #569](https://github.com/temporalio/sdk-core/pull/569))
147+
148+
- [`client`] Throw more specific errors from Client APIs ([#1147](https://github.com/temporalio/sdk-typescript/pull/1147))
149+
150+
### Bug Fixes
151+
152+
- [`core`] Metrics that should be produced by the SDK Core's internal Client would previously not
153+
get emited. This has been fixed. ([#1119](https://github.com/temporalio/sdk-typescript/pull/1119))
154+
- [`client`] Fix incorrect schedule spec boundaries checks on hour and day of month ([#1120](https://github.com/temporalio/sdk-typescript/pull/1120))
155+
- [`workflow`] We know throw more meaningful errors when Workflow-only APIs are used from
156+
non-Workflow context, and some other situations. ([#1126](https://github.com/temporalio/sdk-typescript/pull/1126))
157+
- Removed most `instanceof` checks from SDK, and remplaced them by `XxxError.is(...)` checks, based on the presence of
158+
a symbol property. We believe this should help resolve most of the problems that previously been observed when
159+
multiple copies or different versions of SDK packages are installed in a same project (([#1128](https://github.com/temporalio/sdk-typescript/pull/1128))).
160+
- [`workflow`] Make Local Activily timeouts in `ActivityInfo` match those of non-Local Activities ([#1133](https://github.com/temporalio/sdk-typescript/pull/1133), [Core #569](https://github.com/temporalio/sdk-core/pull/569)).
161+
- [`workflow`] Ensure payload converters keep Uint8Array type equality ([#1143](https://github.com/temporalio/sdk-typescript/pull/1133))
162+
- Fail workflow task if local activity not registered with worker ([#1152](https://github.com/temporalio/sdk-typescript/pull/1152))
163+
- [`core`] Don't increment terminal command metrics when replaying ([Core #572](https://github.com/temporalio/sdk-core/pull/572))
164+
- [`core`] Fix start-to-close local activity timeouts not being retryable like they should be ([#Core 576](https://github.com/temporalio/sdk-core/pull/576))
165+
166+
### Documentation
167+
168+
- Improve documentation for activity heartbeat and cancellationSignal ([#1151](https://github.com/temporalio/sdk-typescript/pull/1151))
169+
7170
## [1.7.4] - 2023-04-27
8171

9172
### Bug Fixes

packages/activity/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ export class Context {
255255
* `ActivityInboundLogInterceptor` with your custom Logger. You may also subclass `ActivityInboundLogInterceptor` to
256256
* customize attributes that are emitted as metadata.
257257
*/
258-
public logger: Logger;
258+
public log: Logger;
259259

260260
/**
261261
* **Not** meant to instantiated by Activity code, used by the worker.
@@ -273,7 +273,7 @@ export class Context {
273273
this.cancelled = cancelled;
274274
this.cancellationSignal = cancellationSignal;
275275
this.heartbeatFn = heartbeat;
276-
this.logger = logger;
276+
this.log = logger;
277277
}
278278

279279
/**

packages/client/src/task-queue-client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export class TaskQueueClient extends BaseClient {
133133
* `NotFetched` entry in {@link BuildIdReachability.taskQueueReachability}. The caller may issue
134134
* another call to get the reachability for those task queues.
135135
*/
136-
public async getBuildIdReachability(options: ReachabilityOptions): Promise<ReachabilityResponse> {
136+
public async getReachability(options: ReachabilityOptions): Promise<ReachabilityResponse> {
137137
let resp;
138138
const buildIds = options.buildIds?.map((bid) => {
139139
if (bid === UnversionedBuildId) {
@@ -167,7 +167,7 @@ export class TaskQueueClient extends BaseClient {
167167
}
168168

169169
/**
170-
* Options for {@link TaskQueueClient.getBuildIdReachability}
170+
* Options for {@link TaskQueueClient.getReachability}
171171
*/
172172
export type ReachabilityOptions = RequireAtLeastOne<BaseReachabilityOptions, 'buildIds' | 'taskQueues'>;
173173

packages/core-bridge/src/conversions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
335335
match WorkerConfigBuilder::default()
336336
.worker_build_id(js_value_getter!(cx, self, "buildId", JsString))
337337
.client_identity_override(Some(js_value_getter!(cx, self, "identity", JsString)))
338-
.use_worker_versioning(js_value_getter!(cx, self, "useWorkerVersioning", JsBoolean))
338+
.use_worker_versioning(js_value_getter!(cx, self, "useVersioning", JsBoolean))
339339
.no_remote_activities(!enable_remote_activities)
340340
.max_outstanding_workflow_tasks(max_outstanding_workflow_tasks)
341341
.max_outstanding_activities(max_outstanding_activities)

packages/core-bridge/ts/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export interface WorkerOptions {
276276
*
277277
* For more information, see https://docs.temporal.io/workers#worker-versioning
278278
*/
279-
useWorkerVersioning: boolean;
279+
useVersioning: boolean;
280280

281281
/**
282282
* The task queue the worker will pull from

packages/test/src/test-activity-log-interceptor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ test("Activity Context's logger defaults to Runtime's Logger", async (t) => {
3030
const env = new MockActivityEnvironment();
3131
const logs: LogEntry[] = await env.run(async () => {
3232
const ctx = activity.Context.current();
33-
ctx.logger.debug('log message from activity');
33+
ctx.log.debug('log message from activity');
3434
return (ctx as MyTestActivityContext).logs;
3535
});
3636
const activityLogEntry = logs.find((entry) => entry.message === 'log message from activity');
@@ -44,7 +44,7 @@ test("Activity Log Interceptor dont override Context's logger by default", async
4444
const ctx = activity.Context.current();
4545
const interceptor = new ActivityInboundLogInterceptor(ctx);
4646
const execute = composeInterceptors([interceptor], 'execute', async () => {
47-
ctx.logger.debug('log message from activity');
47+
ctx.log.debug('log message from activity');
4848
});
4949
try {
5050
await execute({ args: [], headers: {} });
@@ -87,7 +87,7 @@ async function runActivity(
8787

8888
test('ActivityInboundLogInterceptor overrides Context logger if specified', async (t) => {
8989
const logs = await runActivity(async () => {
90-
activity.Context.current().logger.debug('log message from activity');
90+
activity.Context.current().log.debug('log message from activity');
9191
});
9292
t.is(logs.length, 3);
9393
const [_, midLog] = logs;

packages/test/src/test-ephemeral-server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Worker } from './helpers';
66

77
interface Context {
88
bundle: WorkflowBundle;
9+
taskQueue: string;
910
}
1011

1112
const test = anyTest as TestFn<Context>;
@@ -14,9 +15,13 @@ test.before(async (t) => {
1415
t.context.bundle = await bundleWorkflowCode({ workflowsPath: require.resolve('./workflows') });
1516
});
1617

18+
test.beforeEach(async (t) => {
19+
t.context.taskQueue = t.title.replace(/ /g, '_');
20+
});
21+
1722
async function runSimpleWorkflow(t: ExecutionContext<Context>, testEnv: TestWorkflowEnvironment) {
1823
try {
19-
const taskQueue = 'test';
24+
const { taskQueue } = t.context;
2025
const { client, nativeConnection, namespace } = testEnv;
2126
const worker = await Worker.create({
2227
connection: nativeConnection,

packages/test/src/test-isolation.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { randomUUID } from 'crypto';
22
import { TestFn, ImplementationFn } from 'ava';
33
import { TestWorkflowEnvironment } from '@temporalio/testing';
44
import { ApplicationFailure, arrayFromPayloads } from '@temporalio/common';
5+
import { bundleWorkflowCode, WorkflowBundle } from '@temporalio/worker';
56
import { sleep } from '@temporalio/workflow';
67
import { WorkflowFailedError } from '@temporalio/client';
78
import { test as anyTest, bundlerOptions, Worker, REUSE_V8_CONTEXT } from './helpers';
89

910
interface Context {
1011
env: TestWorkflowEnvironment;
1112
taskQueue: string;
13+
workflowBundle: WorkflowBundle;
1214
createWorker(): Promise<Worker>;
1315
}
1416

@@ -23,20 +25,19 @@ const withReusableContext = test.macro<[ImplementationFn<[], Context>]>(async (t
2325
});
2426

2527
test.before(async (t) => {
26-
const env = await TestWorkflowEnvironment.createLocal();
27-
const taskQueue = 'test';
28-
async function createWorker() {
28+
t.context.env = await TestWorkflowEnvironment.createLocal();
29+
t.context.workflowBundle = await bundleWorkflowCode({ workflowsPath: __filename, ...bundlerOptions });
30+
});
31+
32+
test.beforeEach(async (t) => {
33+
t.context.taskQueue = t.title.replace(/ /g, '_');
34+
t.context.createWorker = async () => {
35+
const { env, workflowBundle, taskQueue } = t.context;
2936
return await Worker.create({
3037
connection: env.nativeConnection,
31-
workflowsPath: __filename,
38+
workflowBundle,
3239
taskQueue,
33-
bundlerOptions,
3440
});
35-
}
36-
t.context = {
37-
env,
38-
taskQueue,
39-
createWorker,
4041
};
4142
});
4243

0 commit comments

Comments
 (0)