dbf9b4e7e5
On a warm worker process, a task whose `task()` definition is loaded via `await import(...)` from inside another task's `run()` could end up permanently missing from the catalog: the `task()` call fired with no `_currentFileContext` set, `registerTaskMetadata` silently returned, and Node's ESM module cache then blocked the worker's setContext + re-import recovery from ever firing the call again. Subsequent runs of that task on the same warm process failed with `COULD_NOT_FIND_EXECUTOR` until the process hit `maxExecutionsPerProcess` and exited. All five of these had to coincide on the same worker for the bug to surface: 1. `processKeepAlive` enabled (so catalog state survives across runs). 2. A `run()` function (or lifecycle hook) does `await import(...)`. 3. The import's transitive static graph reaches a `task()` / `schemaTask()` call. 4. The task containing the dynamic import is the **first** task to run on a given warm worker process — so the dropped `task()` calls fire on this process for the first time, are silently dropped, and Node's module cache locks the wrong outcome in. 5. A subsequent run for one of the dropped task ids lands on the same warm worker before it recycles. The runtime workers now set a sentinel file context (`<no-context>`) around the `executor.execute(...)` call, so `task()` invocations firing during a run register normally. The catalog detects the sentinel and emits a one-time `console.warn` per task id so the pattern stays visible without spamming. The indexer never sets this context, so deploy-time behavior is unchanged. Repro is `references/hello-world/src/trigger/dynamicImportRepro*.ts`. Verified end-to-end against a deployed image with firestarter warm-starts on: pre-fix saw `COULD_NOT_FIND_EXECUTOR` on children that landed on the parent-poisoned worker; post-fix all 23/23 runs succeeded and the warning surfaces in the parent's run trace.
20 lines
616 B
JavaScript
20 lines
616 B
JavaScript
// Fixture mimicking a task entrypoint file: top-level code calls into the
|
|
// catalog (the same way `task()` / `schemaTask()` does via
|
|
// `registerTaskMetadata`).
|
|
//
|
|
// Loaded via `await import()` from inside a test that simulates the worker
|
|
// running a task. The point is to exercise top-level evaluation through Node's
|
|
// ESM module loader so the module-cache semantics are real.
|
|
|
|
const register = globalThis.__catalogRegisterTaskMetadata;
|
|
if (typeof register === "function") {
|
|
register({
|
|
id: "lazy-task",
|
|
fns: {
|
|
run: async () => "ok",
|
|
},
|
|
});
|
|
}
|
|
|
|
export const lazyTask = { id: "lazy-task" };
|