Repository navigation
3.8.0
Durable Workflow Server 3.8.0.
| Git tag | 3.8.0 → 28f084513235f6647e740c4b3d9e3aace7a323b7 |
| Image index | sha256:7988a23525d9e50e997a9f6f02e801300ed4f626de3cf6977033657ae7e9f58e in durableworkflow/server and ghcr.io/durable-workflow/server |
| Release run | https://github.com/durable-workflow/server/actions/runs/43323617904 |
Durable Workflow Server 3.8.0 (native)
Server 3.8.0 lets an operator pause activity dispatch on one task queue, lets
redrive reuse side effects and version markers, and removes database round
trips from every workflow task. Its exact images are
durableworkflow/server:3.8.0 and ghcr.io/durable-workflow/server:3.8.0;
the published Compose default selects 3.8.0. Rolling aliases move separately
after published-image qualification.
Upgrade
Schema upgrade required on every backend. This release adds one
native-only table, dw_task_queue_pauses, which is created empty:
| Backend | Schema |
|---|---|
| SQLite | 11 → 12 |
| PostgreSQL | 12 → 13 |
| MariaDB | 12 → 13 |
| MySQL | 13 → 14 |
The step only creates the table; it rewrites no rows and takes seconds. A
server never upgrades its schema at startup. Started on the earlier schema,
3.8.0 refuses with native_schema_upgrade_required.
For the single-node Compose deployment:
-
Stop the Server and leave MySQL running:
docker compose --env-file durable-workflow.prod.env \ -f docker-compose.published.yml stop server
-
Back up MySQL and
server_storage, and record the current image
reference. This backup is the only way back to 3.7.x. -
Change
DW_SERVER_IMAGEto the 3.8.0 image, pull it and run
schema-upgradeonce:docker compose --env-file durable-workflow.prod.env \ -f docker-compose.published.yml pull server docker compose --env-file durable-workflow.prod.env \ -f docker-compose.published.yml run --rm --no-deps server schema-upgrade
From 3.7.x on MySQL it reports
"from_schema_version":13,"schema_version":14,"status":"upgraded". Run
again, it reports"status":"current". -
Start the Server with
docker compose --env-file durable-workflow.prod.env -f docker-compose.published.yml up -d --wait,
then confirm/api/ready,/api/cluster/info(versionis3.8.0) and
worker registration.
With several Servers on one database, stop every one of them before step 3
and start them all on 3.8.0 afterwards. On MySQL and MariaDB, an interrupted
upgrade resumes when you run schema-upgrade again; SQLite and PostgreSQL
upgrade in one transaction.
Rolling back to 3.7.x requires restoring the backup. 3.7.0 refuses the
upgraded database with native_schema_newer_than_binary.
From earlier releases, the 3.8.0 image's schema-upgrade applies every
earlier step in the same run. On MySQL from 3.5.x or 3.6.x (schema 12), that
includes the 3.7.0 JSON rebuild, with its downtime of
about 2 minutes per million history rows. From 3.4.x or earlier, it includes
the 3.5.0 step.
On Kubernetes, scale the Server to zero, back up, run helm upgrade with
native.schemaUpgrade.enabled=true so the 3.8.0 image runs schema-upgrade
once, then scale back up. Chart 1.6.0, which defaults to Server 3.8.0, adds
its notes to the chart's
upgrade notes.
The worker protocol stays at 1.20 and the control plane at version 2.
Pause activity dispatch on a task queue
An operator can hold new activity dispatch on one task queue of one namespace
and later resume it
(#577):
POST /api/task-queues/{task_queue}/activity-dispatch/pause, with an
optionalreasonof up to 1000 characters, answersoutcome: pausedor
already_paused.POST /api/task-queues/{task_queue}/activity-dispatch/resumeanswers
outcome: resumedornot_paused.GET /api/task-queues/{task_queue}/activity-dispatchreads the state.
Task queue describe and list carry the sameactivity_dispatchobject, and
a paused queue stays listed.
activity_dispatch reports state, paused, reason, paused_at,
paused_by, resumed_at and resumed_by. Both principals are the
authenticated caller, never one named in the request. The routes need the
operator or admin role, like build-id drain, so a worker token gets 403. They
work in default and in any named namespace, and a credential bound to one
namespace is refused for the others. Queue names compare exactly, so pausing
Orders does not pause orders.
While a queue is paused, no activity task on it is leased. Ready tasks stay
ready with no attempt used, and after the resume the same task is leased as
attempt 1. Activity deadlines keep running, so a schedule-to-start or
schedule-to-close timeout still fires. Activities already running finish
normally, and a retry they schedule waits for the resume. Workflow, signal,
query and update delivery is unchanged, and the worker protocol too: a held
activity poll times out empty. The pause survives restarts and applies to
every node. A concurrency cap of 0 is not a pause.
Cluster info advertises capabilities.task_queue_activity_dispatch_pause: true. The control-plane OpenAPI document moves from revision 14 to 15 for
the two new paths. See
Task queue activity dispatch pause.
Redrive reuses side effects and version markers
Redrive no longer refuses a failed run because its history records side
effects or version markers before the failed step
(#980). As Workflow
does, the new run records each of them with the original payload,
reused_from_run_id and reused_recorded_at, the time the step first ran.
Workflow time after those steps matches the failed run, including across a
chain of redrives.
A run whose workflow code upserted a memo or search attribute now refuses
with service_redrive_history_not_qualified (previously unsupported_history)
until the SDKs can replay reused upserts. A side effect whose result is stored
externally refuses with external_side_effect_result_not_supported. Describe's
can_redrive and redrive_blocked_reason report the same decision. Runs with
fired timers still refuse with unsupported_history; later slices of #980
cover them.
Fewer database round trips per workflow task
- Each workflow task runs three fewer statements: the sticky-execution record
no longer reads the run status and the worker's process metrics again, and
the replay clock counts redrives in the same read as the opening events
(#1171). - A workflow poll's binding and answer are written to
dw_query_cachein one
statement instead of two
(#1063). - On MySQL, MariaDB and PostgreSQL, each pool connection now caches up to 200
prepared statements instead of SQLx's 100, which holds the hot path of about
160 statements. On MySQL, prepares and closes per workflow drop from about
25 to 0.2 (#1174).
Check MySQL's prepared-statement budget. Every client's prepared
statements count toward the server-wide max_prepared_stmt_count (default
16382). A native node has 4 pool connections, so at the new default it holds
up to 800, and the cluster needs
nodes × 4 × capacity + other clients' statements ≤ max_prepared_stmt_count
That is up to 20 native nodes on a default MySQL server. For more, raise
max_prepared_stmt_count or lower the capacity with
DW_NATIVE_DB_STATEMENT_CACHE_CAPACITY (or statement-cache-capacity in
DB_URL), a whole number from 0 to 4095. A Server given an invalid value, or
two different values, refuses to start. Prepared_stmt_count in
SHOW GLOBAL STATUS reports current use. SQLite is unchanged. See
Prepared-statement cache.
Other changes
- Tests now cover registering a namespace's first search attribute while
workflows upsert other keys, and deleting or re-registering one while a
workflow uses it. Behavior is unchanged from 3.5.0
(#571).