Skip to content

Releases: mafilus/durabletask-go

v1.2.1: execution isolation and gRPC security fixes

Choose a tag to compare

@github-actions github-actions released this 01 Oct 19:40

v1.2.1: execution isolation and gRPC security fixes

This maintenance release fixes correctness and security issues discovered after
v1.2.0. It does not change the public Go or gRPC API, generated protobufs, or
database schema.

  • Fence child-workflow results by the execution captured when the child starts.
    A result from an older parent execution cannot complete a replacement
    execution after cancellation, purge, or recreation.
  • Generate new automatic child instance IDs from the full scheduling action and
    the parent execution, preventing collisions across generations and actions.
    Existing retry histories retain their recorded child IDs.
  • Preserve workflow execution identity through SQL admission, caches, delayed
    starts, cancellation cleanup, and transport retry handling.
  • Update google.golang.org/grpc from v1.83.0 to v1.83.2. This fixes
    GO-2026-6348 and
    GO-2026-6443. The resolved indirect
    golang.org/x/net and golang.org/x/text modules also advance to v0.58.0
    and v0.41.0 respectively.

Upgrade notes

No schema migration is required. Before deploying this version, drain or migrate
queued legacy child results that lack captured parent-execution provenance.
Coordinated SQL writers must be upgraded together: an ambiguous legacy result is
rejected rather than being delivered to a potentially different execution. See
the child execution isolation guide
for the operational boundary.

Validation

The execution-isolation runtime commit
a21db2d
passed the full local suite, go vet ./..., targeted race checks, and isolated
PostgreSQL child/cancellation and purge/recreation races. The follow-up chaos
test commit
12133d3
ran all five isolated PostgreSQL crash/restart scenarios successfully. Its
runtime content was unchanged from a21db2d; the helper confirms the crash
before restarting the same container. Its 60-second observation window preserves
the pre-expiry and post-expiry assertions while accommodating measured PostgreSQL
recovery.

The dependency-only commit
8a7f7c4
passed go test ./... and govulncheck -show verbose ./... with no reported
vulnerabilities. Its build
and OpenTelemetry collector
checks completed successfully. These checks are evidence for this source release;
they do not prove a downstream deployment or migration was performed.

v1.2.0: worker transport reliability

Choose a tag to compare

@github-actions github-actions released this 09 Sep 17:07

v1.2.0: worker transport reliability

This series follows the maintenance backports at
e69d246f575e899700528027d927fbcf41f7b6b1.

  • Preserve task resolutions that arrive before the corresponding task is
    registered during replay, without dispatching already resolved work again.
  • Issue a fresh completion token for each gRPC dispatch attempt and validate it
    before forwarding a worker response to the backend. The Go worker echoes the
    token on both success and failure responses.
  • Recover attempts owned by a disconnected stream through backend abandonment
    and reacquisition. Preserve the registry reservation until any in-flight
    backend completion finishes, so stale cleanup cannot affect a successor.
  • Clean up canceled local waiters conditionally and make executor shutdown
    idempotent without closing a queue while producers may still send.

The public synchronous Backend and Executor interfaces, generated protobufs,
and database schemas are unchanged. No schema migration is required.

Histories with unresolved legacy timer numbering retain the ambiguous work until
a scheduling event confirms the task IDs. If no such evidence arrives, replay
remains waiting with a warning; the engine does not guess which task owns a
response or publish work with provisional IDs.

Worker rollout

Empty-token responses remain accepted by default for compatibility. That mode
cannot fence stale responses from legacy workers. Upgrade every worker before
requiring completion tokens on the server:

executor, register := backend.NewGrpcExecutor(
    be, logger, backend.WithRequireCompletionTokens(),
)

In strict mode, a worker that does not echo tokens cannot complete its work.
Completion-token validation does not provide exactly-once external side effects;
activities still require external idempotency when effects can be retried.

See the transport design and proof boundary
for upstream commit provenance and compatibility details.

Validation (2026-09-09)

  • The complete suite passed with PostgreSQL enabled:
    go test -p 1 -count=1 -timeout=3m ./....
  • Race-detector checks passed for the task replay tests, executor transport
    regressions, local waiters, client, and SQLite/gRPC recovery test.
  • All five PostgreSQL crash/restart scenarios passed in an isolated Docker
    instance; go vet ./... and git diff --check passed.
  • Independent cross-review finished with no remaining findings after correcting
    ambiguous legacy timer IDs, detached-workflow dispatch, and shutdown blocking
    behind a slow backend completion.

These local checks used Go 1.27.0-X:nodwarf5 and PostgreSQL 17. The transport
recovery test uses SQLite on disk and gRPC bufconn; it proves abandonment,
reacquisition, stale-token rejection, and terminal completion locally, not a
deployment or a real network partition. PostgreSQL crash tests are a separate
backend durability proof.

The runtime baseline faccb3cbb050ba69e72569f664c617154df559a4 also passed
PR Validation
and OpenTelemetry integration.
Release preparation changes documentation and adds the tag-triggered publication
workflow only; the validated runtime is unchanged.

v1.1.1: workflow termination and execution identity

Choose a tag to compare

@github-actions github-actions released this 09 Sep 17:05

v1.1.1: workflow termination and execution identity

This maintenance update backports two Dapr 0.12 fixes onto
3c63ac32ce3df69ad616dfdc6a67ebe39e4341c9 without changing public interfaces,
protobuf definitions, or database schemas.

Workflow termination

Source: c1b1fb8d3694fba6c404d1fa54892d1eb12f3fff.

Termination remains effective when later events occur in the same work item,
while suspended, and when competing with ContinueAsNew. The Go executor stops
processing subsequent events and emits actions in sequence order. An earlier
terminal completion wins; ContinueAsNew cannot override termination. Applying
multiple completion actions does not duplicate the parent notification.

The runtime also enforces termination when a worker ignores it. The backport
additionally suppresses ContinueAsNew before it replaces the runtime history,
preserving the original execution identity and the child records needed for
recursive termination. A regression test covers both the parent notification
and a child routed to another application and namespace.

The upstream worker tests use the fork's context-taking StopAndDrain API.

Execution identity

Source: c4a98f73b12c22afc4af8b4248636305d78bda90.

The gRPC executor populates WorkflowRequest.ExecutionId from ExecutionStarted,
looking in new events before past events. Workers can distinguish generations
of the same instance while retaining stable identity during replay. Histories
without an execution ID continue to produce a nil value. The upstream test
import is adapted to github.com/mafilus/durabletask-go.

CI and scope

All 13 third-party action references in the four GitHub Actions workflows are
pinned to full upstream commit SHAs, retaining their existing major versions.

This update does not port the asynchronous worker architecture, early task
resolution handling, CompletionToken validation, or stream-disconnect recovery.
Those changes require a separate compatibility and transport reliability review.

Local validation (2026-09-09)

  • Go 1.27.0-X:nodwarf5, PostgreSQL 17 in dedicated Docker containers.
  • go test -p 1 -count=1 -timeout=3m ./... passed with PostgreSQL enabled.
  • The PostgreSQL package suite and all five TestIntegrationPostgresRestart
    scenarios passed, including crash after commit before acknowledgement.
  • Targeted termination and execution-ID regressions, go vet ./..., and
    git diff --check passed. Independent review found no remaining findings.

An initial build lost its temporary directory under /tmp. A subsequent parallel
suite failed in an existing retry-timing test and the gRPC package; the retry
test passed 20 isolated repetitions, and the complete sequential run passed.
The cause of the initial gRPC failure was not established. These results are
local validation, not a GitHub Actions run or a published release.

Release verification

The runtime baseline e69d246f575e899700528027d927fbcf41f7b6b1 passed
PR Validation
and OpenTelemetry integration.
Release preparation changes only this note and the tag-triggered publication
workflow; the validated runtime is unchanged.

v1.1.0 — idempotent external event delivery

Choose a tag to compare

@mafilus mafilus released this 26 Aug 02:57

External event delivery deduplication

RaiseEvent supports a durable external delivery ID:

client.RaiseEvent(ctx, instanceID, "order.created",
    api.WithEventPayload(payload),
    api.WithExternalDeliveryID(messageID),
)

SQLite and PostgreSQL deduplicate a repeated non-empty delivery ID per
(instanceID, eventName, deliveryID). A retry after a publisher crash succeeds
without appending a second EventRaised event.

PostgreSQL upgrade

Before enabling external delivery IDs, apply:

psql "$DATABASE_URL" -v ON_ERROR_STOP=1
-f docs/migrations/postgresql/v1.1.0_external_event_deliveries.sql

Apply the migration before deploying v1.1.0 servers, then enable publishers.
Older gRPC servers do not preserve the delivery-ID guarantee.

Compatibility

Existing RaiseEvent calls are unchanged. Explicitly empty delivery IDs are
rejected. Custom backends must implement backend.ExternalEventDeliveryBackend.