Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose OOP invocation errors #2654

Merged
merged 3 commits into from
Nov 9, 2023
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
13 changes: 4 additions & 9 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release Notes

## Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.1.0-preview.2
## Microsoft.Azure.Functions.Worker.Extensions.DurableTask <version>

### New Features

Expand All @@ -10,20 +10,15 @@

### Dependency Updates

`Microsoft.DurableTask.*` to `1.1.0-preview.2`

## Microsoft.Azure.WebJobs.Extensions.DurableTask v2.12.0-preview.1
## Microsoft.Azure.WebJobs.Extensions.DurableTask <version>

### New Features

- Updates to take advantage of new core-entity support

### Bug Fixes

- Fix failed orchestration/entities not showing up as function invocation failures.

### Breaking Changes

### Dependency Updates

`Microsoft.Azure.DurableTask.Core` to `2.16.0-preview.2`
`Microsoft.Azure.DurableTask.AzureStorage` to `1.16.0-preview.2`

Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
#nullable enable
using System;
using System.Collections.Generic;
using DurableTask.Core;
using DurableTask.Core.Command;
using DurableTask.Core.Entities;
using DurableTask.Core.Entities.OperationFormat;
using DurableTask.Core.History;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
{
Expand All @@ -20,9 +17,40 @@ public RemoteEntityContext(EntityBatchRequest batchRequest)
}

[JsonProperty("request")]
public EntityBatchRequest Request { get; private set; }
internal EntityBatchRequest Request { get; private set; }

jviau marked this conversation as resolved.
Show resolved Hide resolved
[JsonIgnore]
internal EntityBatchResult? Result { get; set; }

internal void ThrowIfFailed()
{
if (this.Result == null)
{
throw new InvalidOperationException("Entity batch request has not been processed yet.");
}

if (this.Result.FailureDetails is { } f)
{
throw new EntityFailureException(f.ErrorMessage);
}

List<Exception>? errors = null;
if (this.Result.Results is not null)
{
foreach (OperationResult result in this.Result.Results)
{
if (result.FailureDetails is { } failure)
{
errors ??= new List<Exception>();
errors.Add(new EntityFailureException(failure.ErrorMessage));
}
}
}

if (errors is not null)
{
throw errors.Count == 1 ? errors[0] : new AggregateException(errors);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using DurableTask.Core;
using DurableTask.Core.Command;
using DurableTask.Core.Entities;
using DurableTask.Core.Exceptions;
using DurableTask.Core.History;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand All @@ -18,6 +19,8 @@ internal class RemoteOrchestratorContext

private OrchestratorExecutionResult? executionResult;

private Exception? failure;

public RemoteOrchestratorContext(OrchestrationRuntimeState runtimeState, TaskOrchestrationEntityParameters? entityParameters)
{
this.runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
Expand Down Expand Up @@ -48,6 +51,19 @@ public RemoteOrchestratorContext(OrchestrationRuntimeState runtimeState, TaskOrc
[JsonIgnore]
internal TaskOrchestrationEntityParameters? EntityParameters { get; private set; }

internal void ThrowIfFailed()
{
if (this.failure != null)
{
throw this.failure;
}
}

internal OrchestratorExecutionResult GetResult()
{
return this.executionResult ?? throw new InvalidOperationException($"The execution result has not yet been set using {nameof(this.SetResult)}.");
}

internal void SetResult(IEnumerable<OrchestratorAction> actions, string customStatus)
{
var result = new OrchestratorExecutionResult
Expand Down Expand Up @@ -107,16 +123,24 @@ private void SetResultInternal(OrchestratorExecutionResult result)
this.OrchestratorCompleted = true;
this.SerializedOutput = completeAction.Result;
this.ContinuedAsNew = completeAction.OrchestrationStatus == OrchestrationStatus.ContinuedAsNew;

if (completeAction.OrchestrationStatus == OrchestrationStatus.Failed)
{
string message = completeAction switch
{
{ FailureDetails: { } f } => f.ErrorMessage,
{ Result: { } r } => r,
_ => "Exception occurred during orchestration execution.",
};

this.failure = new OrchestrationFailureException(message);
}

break;
}
}

this.executionResult = result;
}

internal OrchestratorExecutionResult GetResult()
{
return this.executionResult ?? throw new InvalidOperationException($"The execution result has not yet been set using {nameof(this.SetResult)}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;

// TODO: move to DurableTask.Core if needed.
namespace DurableTask.Core.Entities
{
internal class EntityFailureException : Exception
{
public EntityFailureException(string message)
: base(message)
{
}
}
}
9 changes: 6 additions & 3 deletions src/WebJobs.Extensions.DurableTask/OutOfProcMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
using DurableTask.Core.History;
using DurableTask.Core.Middleware;
using Microsoft.Azure.WebJobs.Host.Executors;
using Newtonsoft.Json;
using P = Microsoft.DurableTask.Protobuf;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask
{
Expand Down Expand Up @@ -137,10 +137,12 @@ await this.LifeCycleNotificationHelper.OrchestratorStartingAsync(
}

byte[] triggerReturnValueBytes = Convert.FromBase64String(triggerReturnValue);
var response = Microsoft.DurableTask.Protobuf.OrchestratorResponse.Parser.ParseFrom(triggerReturnValueBytes);
P.OrchestratorResponse response = P.OrchestratorResponse.Parser.ParseFrom(triggerReturnValueBytes);
context.SetResult(
response.Actions.Select(ProtobufUtils.ToOrchestratorAction),
response.CustomStatus);

context.ThrowIfFailed();
},
#pragma warning restore CS0618 // Type or member is obsolete (not intended for general public use)
};
Expand Down Expand Up @@ -326,9 +328,10 @@ void SetErrorResult(FailureDetails failureDetails)
}

byte[] triggerReturnValueBytes = Convert.FromBase64String(triggerReturnValue);
var response = Microsoft.DurableTask.Protobuf.EntityBatchResult.Parser.ParseFrom(triggerReturnValueBytes);
P.EntityBatchResult response = P.EntityBatchResult.Parser.ParseFrom(triggerReturnValueBytes);
context.Result = response.ToEntityBatchResult();

context.ThrowIfFailed();
#pragma warning restore CS0618 // Type or member is obsolete (not intended for general public use)
},
};
Expand Down
Loading