Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:
contents: read

env:
WDL_CLI_PACKAGE: "@wdl-dev/cli@1.8.1"
WDL_CLI_PACKAGE: "@wdl-dev/cli@1.9.0"

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Added bounded Workflow definition pagination, with per-worker limits of 1024 retained names and 1 MiB of definition data. Large metadata snapshots fail before transfer instead of materializing an unbounded namespace view. Update CLI clients to `1.9.0` or later before rolling Control; CI and Quick Start now pin `1.9.0`.
- Bounded Workflow deletion preflight and batched token-fenced cleanup with internal continuation and delete-lock renewal. Large cleanup requests may return `workflow_lifecycle_check_incomplete` and be retried without losing completed cleanup work.
- Accounted retained, active/detached, and in-flight replay bytes under a 64 MiB Runtime budget; saturation is retryable and never becomes a fresh step claim. Persisted result limits apply consistently to list, get, and status; restart rejects invalid stored params before changing state.
- Bounded Workflow instance-list pages to 8 MiB of JSON while preserving payloads, order, and cursor continuation; Control enforces the same response cap and a five-second read deadline without re-encoding successful pages. Update Workflows before system-runtime; no state migration is required.
- Added `system_runtime_desired_count` to Terraform, preserving the default of one while allowing all service families to scale to zero for maintenance.

## wdl.20260906.1 - 2026-09-07

- Updated workerd and Workers types to `1.20260906.1` and `5.20260906.1`, advancing the maximum compatibility date to `2026-09-13`; upstream runtime behavior, experimental flags, static dates, and autogates are unchanged.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ The platform is split into seven app services plus shared state:
| `d1-runtime` | D1 SQLite execution with owner leases and supervised workerd process. |
| `do-runtime` | Durable Object native facets, SQLite storage, alarms, owner leases, and WebSockets. |
| `scheduler` | Cron, queue, and workflow tick dispatch. |
| `workflows` | Workflows V2 state machine, DB 2 owner, and DO alarm delivery backend. |
| `workflows` | Workflow state machine, DB 2 owner, and DO alarm delivery backend. |

Valkey/Redis uses a deliberate logical split:

Expand Down Expand Up @@ -172,7 +172,7 @@ compose bind-mounts `./dist` over the image's built configs.
```bash
git clone https://github.com/wdl-dev/wdl.git
cd wdl
npm install -g @wdl-dev/cli@1.8.1
npm install -g @wdl-dev/cli@1.9.0
npm ci
npm install --ignore-scripts --prefix examples/hello-jsonc
npm run compile:workerd:local
Expand Down
4 changes: 2 additions & 2 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ WDL 首先是基础设施项目:operator 运行自己的 platform,tenant 用
| `d1-runtime` | D1 SQLite execution,使用 owner lease 和 supervisor 管理 workerd process。 |
| `do-runtime` | Durable Object native facet、SQLite storage、alarm、owner lease 和 WebSocket。 |
| `scheduler` | cron、queue 和 workflow tick dispatch。 |
| `workflows` | Workflows V2 state machine、DB 2 owner 和 DO alarm delivery backend。 |
| `workflows` | Workflow state machine、DB 2 owner 和 DO alarm delivery backend。 |

Valkey/Redis 使用明确的逻辑切分:

Expand Down Expand Up @@ -104,7 +104,7 @@ Clone 仓库,安装 standalone `wdl` CLI 和仓库依赖,编译本地 worker
```bash
git clone https://github.com/wdl-dev/wdl.git
cd wdl
npm install -g @wdl-dev/cli@1.8.1
npm install -g @wdl-dev/cli@1.9.0
npm ci
npm install --ignore-scripts --prefix examples/hello-jsonc
npm run compile:workerd:local
Expand Down
9 changes: 8 additions & 1 deletion control/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "shared-workerd-compat-flags";
import { normalizeBindings, validateBindings } from "control-bindings";
import { parseWorkerdDependencyVersion } from "control-lib";
import { WORKFLOW_DEFINITIONS_MAX_COUNT, workflowDeclarationsFit } from "control-workflow-definitions";
import { assertCanonicalBase64 } from "base64.js";
import PACKAGE_JSON_SOURCE from "wdl-package-json-source";

Expand Down Expand Up @@ -196,6 +197,9 @@ export function normalizeWorkflows(workflows) {
if (!Array.isArray(workflows)) {
throw new Error("workflows must be an array of entries");
}
if (workflows.length > WORKFLOW_DEFINITIONS_MAX_COUNT) {
throw new BundleConfigError(413, "workflow_definitions_too_large", "Workflow declaration count exceeds the per-worker limit");
}
const out = [];
const seenNames = new Set();
const seenBindings = new Set();
Expand All @@ -207,7 +211,7 @@ export function normalizeWorkflows(workflows) {
throw new BundleConfigError(
400,
"workflow_script_name_unsupported",
"workflows script_name is not supported in WDL Workflows V2"
"workflows script_name is not supported in WDL Workflows"
);
}
if (typeof entry.name !== "string" || !WORKFLOW_NAME_RE.test(entry.name)) {
Expand Down Expand Up @@ -256,6 +260,9 @@ export function normalizeWorkflows(workflows) {
}
out.push({ name: entry.name, binding: entry.binding, className });
}
if (!workflowDeclarationsFit(out)) {
throw new BundleConfigError(413, "workflow_definitions_too_large", "Workflow declarations exceed the per-worker byte limit");
}
return out;
}

Expand Down
2 changes: 1 addition & 1 deletion control/handlers/delete.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ async function executeWholeDelete({ redis, ns, name, principal, requestId, log,
assertActiveVersionRetained(collected);
let workflowBlocker = null;
try {
await assertWorkflowDeleteAllowed({ ns, worker: name, allowCleanup: true, requestId });
await assertWorkflowDeleteAllowed({ ns, worker: name, allowCleanup: true, requestId, lockToken });
} catch (err) {
if (err instanceof ControlAbort && err.code === "workflow_instances_active") {
workflowBlocker = err;
Expand Down
11 changes: 10 additions & 1 deletion control/handlers/deploy.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { readWorkflowDefinitionAdmission, workflowDeclarationsFit, workflowDefinitionUpdatesFit } from "control-workflow-definitions";
import {
jsonResponse, jsonError, readJsonBody, formatError,
requireControlLog, requireControlRedis,
Expand Down Expand Up @@ -1252,10 +1253,15 @@ async function materializeCommittedMetadata(iso, { ns, name, prepared, resolvedD
const workflowDefUpdates = [];
if (Array.isArray(committedMeta.workflows) && committedMeta.workflows.length) {
const defsKey = workflowDefsKey(ns, name);
const existingDefRaws = await iso.hMGet(
const admission = await readWorkflowDefinitionAdmission(
iso,
defsKey,
committedMeta.workflows.map((workflow) => workflow.name)
);
if (admission.status !== 1) {
throw new DeployAbort(500, "workflow_definition_corrupt", { message: "Stored Workflow definitions exceed their bounds" });
}
const existingDefRaws = admission.values;
for (let index = 0; index < committedMeta.workflows.length; index += 1) {
const workflow = committedMeta.workflows[index];
const existingDef = parsePersistedWorkflowDef(workflow.name, existingDefRaws[index]);
Expand All @@ -1272,6 +1278,9 @@ async function materializeCommittedMetadata(iso, { ns, name, prepared, resolvedD
JSON.stringify({ workflowKey, className: workflow.className }),
]);
}
if (!workflowDeclarationsFit(committedMeta.workflows) || !workflowDefinitionUpdatesFit(admission, workflowDefUpdates.map(([, field, value]) => [field, value]))) {
throw new DeployAbort(413, "workflow_definitions_too_large", { message: "Workflow definitions exceed the per-worker count or byte limit" });
}
}

let doStorageId = null;
Expand Down
2 changes: 1 addition & 1 deletion control/handlers/versions.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ async function handleDelete({ ns, name, version, principal, requestId }) {
let result;
try {
await assertWorkflowDeleteAllowed({
ns, worker: name, version, allowCleanup: true, requestId,
ns, worker: name, version, allowCleanup: true, requestId, lockToken,
});
if (!await renewDeleteLock(redis, ns, name, lockToken)) {
throw new VersionDeleteError(409, "deleting", deleteLockExpiredDetails(ns, name, version));
Expand Down
Loading