-
Notifications
You must be signed in to change notification settings - Fork 145
Description
What are you really trying to do?
Our Temporal TypeScript worker bundles class-based workflows that use Inversify’s decorators (and therefore reflect-metadata). These workflows are instantiated within the workflow entry point using container.get(WorkflowIdentifier).
We implement workflows as classes to enhance workflow and activity modularity and to make it easier to write unit tests.
Describe the bug
When reuseV8Context remains enabled (the default), the workflow is unable to start up with error
TypeError: Cannot define property decorate, object is not extensible
at Function.defineProperty (<anonymous>)
at reflect-metadata/Reflect.js:35:0
at reflect-metadata/Reflect.js:146:0
at reflect-metadata/Reflect.js:29:0
at reflect-metadata/Reflect.js:55:5
This used to work in temporal-sdk 1.11.8, but breaks as soon as we upgrade to a later sdk version.
Temporal freezes the global Reflect object inside the reusable workflow isolate, so reflect-metadata cannot add its helpers.
Setting reuseV8Context to false fixes the issue, but does increase our memory and cpu footprint.
Minimal Reproduction
workflows.ts
import "reflect-metadata";
import { Container, injectable } from "inversify";
@injectable()
class Workflows {
async execute(): Promise<void> {
// This workflow doesn't need to do anything; the error occurs as soon as
// reflect-metadata tries to patch the frozen Reflect object.
}
}
const container = new Container();
container.bind(Workflows).toSelf();
export default async function () {
const workflow = container.get(Workflows);
return await workflow.execute();
}
worker.ts
import path from "node:path";
import { fileURLToPath } from "node:url";
import { NativeConnection, Worker } from "@temporalio/worker";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function main(): Promise<void> {
const reuseV8Context = process.env.REUSE_V8_CONTEXT !== "false";
console.info(
`[repro] Starting worker with reuseV8Context=${reuseV8Context.toString()}`,
);
const connection = await NativeConnection.connect({
address: "localhost:7233",
});
const worker = await Worker.create({
connection,
namespace: "dev",
taskQueue: "dev",
reuseV8Context,
workflowsPath: path.join(__dirname, "workflows.ts"),
});
await worker.run();
}
main().catch((error) => {
console.error("[repro] Worker creation failed with error:");
console.error(error);
process.exitCode = 1;
});
Execute with
1. tsx worker.ts
2. temporal workflow start --task-queue dev --type TestWorkflow
Environment/Versions
- OS and processor: macOS
- Temporal Version: 1.13
- Are you using Docker or Kubernetes or building Temporal from source? No