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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `durabletaskscheduler` token scopes now trim whitespace and all trailing slashes from `Options.ResourceID`. Previously only one trailing slash was removed and untrimmed values produced a malformed scope.
- The owned DTS management client now observes both unary and streaming RPC outcomes for channel recreation. Caller cancellation, caller deadlines, and expected instance-wait deadlines are neutral and no longer reset or poison an existing transport-failure streak.
- `TaskHubGrpcClient.ScheduleNewOrchestration` now sends the caller's W3C trace context on `CreateInstanceRequest`, so the service can parent the `create_orchestration` span to it. Previously every orchestration scheduled over gRPC or the Durable Task Scheduler began a disconnected root trace instead of joining the caller's trace.
- `ContinueAsNew` with `task.WithKeepUnprocessedEvents` no longer drops an external event that arrives in the same work item after the orchestrator returns while a `WaitForSingleEvent` for that name is still pending, such as the loser of a `WhenAny` with a timer. Previously the event completed the unreachable wait and was missing from the next execution. Applications that re-sent such events to work around the loss can now see both copies. A failed or terminated completion no longer carries events forward when `ContinueAsNew` was called earlier in the turn.
- Rejected rewind histories now identify the Go SDK's history-rewrite limitation instead of implying that DTS does not support rewind. The shared unsupported-feature error message is neutral about the source of the limitation; `errors.Is(err, api.ErrFeatureNotSupported)` and rewind safety checks are unchanged.

## [v0.6.0] - 2025-02-05

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ Two rules control the order:
- If more than one live wait uses the same event name, the newest wait gets the next event. This rule is last in, first out. It agrees with the Durable Task .NET replay contract.
- If an event arrives before any wait, DTS keeps the event. The orchestration then reads these events in arrival order. This rule is first in, first out.

A wait stays live until it gets an event, times out, or is canceled. This is true even if the wait lost a `WhenAny` race. A live wait that no code awaits still takes the next event with its name. To keep the event for later code, cancel the losing wait with `ctx.WithCancel`. You can also read events with `task.NewEventChannel` and `ctx.Select`. A channel keeps an event until a `Select` case receives it.

With `task.WithKeepUnprocessedEvents`, `ContinueAsNew` moves the undelivered events to the next execution. This includes an event that arrives in the same work item after the orchestrator function returns, even if a wait for that name is still live.

Full sample: [samples/externalevents](./samples/externalevents).

### Pattern 4: Retries and failure handling
Expand Down
4 changes: 3 additions & 1 deletion api/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ const (
DefaultPurgePollInterval = 100 * time.Millisecond
)

var ErrFeatureNotSupported = errors.New("feature is not supported by Durable Task Scheduler")
// ErrFeatureNotSupported identifies a limitation in the SDK or service.
// The surrounding error describes which operation or history shape is unsupported.
var ErrFeatureNotSupported = errors.New("feature is not supported")
Comment thread
torosent marked this conversation as resolved.

// OrchestrationQuery describes a bounded query for orchestration instances.
// ContinuationToken values are opaque and must only be reused with the same query.
Expand Down
17 changes: 17 additions & 0 deletions client/grpc_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ func TestClientRPCErrorMappingsPreserveStatus(t *testing.T) {
}
}

func TestClientRPCUnsupportedFeaturePreservesServiceReason(t *testing.T) {
const reason = "server does not expose QueryInstances"
err := clientRPCError(context.Background(), "query instances", status.Error(codes.Unimplemented, reason))
if !errors.Is(err, api.ErrFeatureNotSupported) {
t.Fatalf("error %v does not match ErrFeatureNotSupported", err)
}
if status.Code(err) != codes.Unimplemented {
t.Fatalf("status.Code() = %v, want Unimplemented", status.Code(err))
}
if !strings.Contains(err.Error(), reason) {
t.Fatalf("error lost the service's reason: %v", err)
}
if strings.Contains(err.Error(), "not supported by Durable Task Scheduler") {
t.Fatalf("the shared error category should not attribute the limitation to DTS: %v", err)
}
}

func TestClientRPCErrorPrefersCallerContext(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
Expand Down
3 changes: 2 additions & 1 deletion client/grpc_worker_rewind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,6 @@ func TestWorkerRewindRejectsHandledFailureWithoutReplacement(t *testing.T) {
require.Nil(t, response.Actions[0].GetRewindOrchestration())
completion := response.Actions[0].GetCompleteOrchestration()
require.Equal(t, api.RUNTIME_STATUS_FAILED, completion.GetOrchestrationStatus())
require.Contains(t, completion.GetFailureDetails().GetErrorMessage(), "rewind cannot remove an operation failure")
require.Contains(t, completion.GetFailureDetails().GetErrorMessage(), "Go SDK cannot safely rewind this history")
require.NotContains(t, completion.GetFailureDetails().GetErrorMessage(), "not supported by Durable Task Scheduler")
}
2 changes: 2 additions & 0 deletions durabletaskscheduler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ unsupported history in its logs without emitting replacement history; the
instance may retain its original failure metadata. Enqueue success does not
imply that this worker-side validation succeeded. This guard is not a
general proof of replay safety and does not infer whether a failure was handled.
These history rejections describe a Go SDK limitation, not a lack of rewind
support in DTS. They retain the `api.ErrFeatureNotSupported` error category.
Rewind does not repair unchanged failing application code. Replacement history
uses the normal large-payload handling and orchestration response-size bound;
oversized responses fail explicitly rather than being silently truncated.
Expand Down
Loading
Loading