public async Task Execute( [OrchestrationTrigger] IDurableOrchestrationContext orchestratorContext, ILogger log) { if (orchestratorContext is null) { throw new ArgumentNullException(nameof(orchestratorContext)); } if (log is null) { throw new ArgumentNullException(nameof(log)); } var command = orchestratorContext.GetInput <ICommand>(); var commandResult = command.CreateResult(); try { await orchestratorContext .AuditAsync(command, commandResult) .ConfigureAwait(true); var commandHandler = commandHandlers .SingleOrDefault(ch => ch.Orchestration && ch.CanHandle(command)); if (commandHandler is null) { throw new NullReferenceException($"Could not find orchestration handler for command '{command.GetType()}'"); } await orchestratorContext .AuditAsync(command, commandResult) .ConfigureAwait(true); commandResult = await commandHandler .HandleAsync(command, new CommandCollector(orchestratorContext, command), orchestratorContext, log) .ConfigureAwait(true); if (commandResult is null) { throw new NullReferenceException($"Command handler '{commandHandler.GetType()}' returned NULL result"); } } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { await orchestratorContext .AuditAsync(command, commandResult) .ConfigureAwait(true); orchestratorContext.SetOutput(commandResult); } }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } var command = functionContext.GetInput <ICommand>(); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); var commandOrchestration = await functionContext .CallActivityWithRetryAsync <string>(nameof(OrchestratorCommandDispatchActivity), command) .ConfigureAwait(true); commandResult = await functionContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestration, command.CommandId.ToString(), command) .ConfigureAwait(true); } catch (Exception exc) { commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}"); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { if (commandResult?.RuntimeStatus.IsUnknown() ?? false) { commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultAugmentActivity), commandResult) .ConfigureAwait(true); } await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); functionContext.SetOutput(commandResult); } }
private static async Task <ICommandResult> SwitchCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log) { try { await functionContext .AuditAsync(command, commandResult, provider) .ConfigureAwait(true); functionContext.SetCustomStatus($"Switching command", log); var project = await functionContext .GetProjectAsync(command.ProjectId, allowUnsafe : true) .ConfigureAwait(true); functionContext.ContinueAsNew(( new ProviderProjectUpdateCommand ( command.BaseApi, command.User as Model.Data.User, project.PopulateExternalModel(), command.CommandId), provider ) ); } catch (Exception exc) { functionContext.SetCustomStatus($"Switching command failed: {exc.Message}", log, exc); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } return(commandResult); }
public static async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, [DurableClient] IDurableClient durableClient, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } if (durableClient is null) { throw new ArgumentNullException(nameof(durableClient)); } var commandMessage = functionContext.GetInput <ProviderCommandMessage>() ?? throw new ArgumentException("Command message is null", nameof(functionContext)); var command = commandMessage.Command ?? throw new ArgumentException("Command message does not contain a command", nameof(functionContext)); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); var commandOrchestrationName = await functionContext .CallActivityWithRetryAsync <string>(nameof(ProviderCommandDispatchActivity), command) .ConfigureAwait(true); var commandOrchestrationInstanceId = CommandHandler.GetCommandOrchestrationInstanceId(command); if (string.IsNullOrEmpty(commandOrchestrationName)) { commandLog .LogInformation($"Dispatching command '{command.GetType().FullName}' ({command.CommandId}) >>> FALLBACK ({commandOrchestrationInstanceId})"); commandResult = await functionContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(nameof(ProviderCommandFallbackOrchestration), commandOrchestrationInstanceId, command) .ConfigureAwait(true); } else { commandLog .LogInformation($"Dispatching command '{command.GetType().FullName}' ({commandMessage.CommandId}) >>> {commandOrchestrationName} ({commandOrchestrationInstanceId})"); commandResult = await functionContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestrationName, commandOrchestrationInstanceId, command) .ConfigureAwait(true); } commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(ProviderCommandResultAugmentActivity), (commandResult, commandOrchestrationInstanceId)) .ConfigureAwait(true); } catch (Exception exc) { commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}"); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { try { await functionContext .CallActivityWithRetryAsync(nameof(ProviderCommandResultSendActivity), (commandResult, commandMessage.CallbackUrl)) .ConfigureAwait(true); } catch (Exception exc) { commandLog.LogError(exc, $"Sending result for command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}"); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); functionContext.SetOutput(commandResult); } } }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext, ILogger log) { if (orchestrationContext is null) { throw new ArgumentNullException(nameof(orchestrationContext)); } var command = orchestrationContext.GetInput <ICommand>(); var commandResult = command.CreateResult(); var commandLog = orchestrationContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { orchestrationContext.SetCustomStatus("Auditing command", log); await orchestrationContext .AuditAsync(command, commandResult) .ConfigureAwait(true); orchestrationContext.SetCustomStatus("Processing command", log); commandResult = await orchestrationContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(OrchestratorCommandOrchestrationHandler.GetCommandOrchestrationName(command), command.CommandId.ToString(), command) .ConfigureAwait(true); } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { try { orchestrationContext.SetCustomStatus("Augmenting command result", log); commandResult = await orchestrationContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultAugmentActivity), commandResult) .ConfigureAwait(true); } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } orchestrationContext.SetCustomStatus("Auditing command result", log); await orchestrationContext .AuditAsync(command, commandResult) .ConfigureAwait(true); var commandException = commandResult.Errors?.ToException(); if (commandException is null) { orchestrationContext.SetCustomStatus($"Command succeeded", log); } else { orchestrationContext.SetCustomStatus($"Command failed: {commandException.Message}", log, commandException); } orchestrationContext.SetOutput(commandResult); } }
public static async Task <ICommandResult> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } var(command, provider) = functionContext.GetInput <(IProviderCommand, Provider)>(); var commandResult = command.CreateResult(); var commandMessage = default(ICommandMessage); var commandCallback = default(string); try { functionContext.SetCustomStatus($"Acquire callback url for command '{command.CommandId}'", log); commandCallback = await functionContext .CallActivityWithRetryAsync <string>(nameof(CallbackAcquireActivity), (functionContext.InstanceId, command)) .ConfigureAwait(true); commandMessage = new ProviderCommandMessage(command, commandCallback); functionContext.SetCustomStatus($"Prepare sending command '{command.CommandId}'", log); await RegisterProviderAsync(functionContext, command, provider) .ConfigureAwait(true); await EnableProviderAsync(functionContext, command, provider) .ConfigureAwait(true); command = await AugmentCommandAsync(functionContext, provider, command) .ConfigureAwait(true); await functionContext .AuditAsync(provider, command, commandResult) .ConfigureAwait(true); try { functionContext.SetCustomStatus($"Sending command '{command.CommandId}'", log); commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), (provider, commandMessage)) .ConfigureAwait(true); } catch (RetryCanceledException) { commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultActivity), (provider, commandMessage)) .ConfigureAwait(true); } finally { await functionContext .AuditAsync(provider, command, commandResult) .ConfigureAwait(true); } if (commandResult.RuntimeStatus.IsActive()) { var commandTimeout = (commandResult.Timeout > TimeSpan.Zero && commandResult.Timeout < CommandResult.MaximumTimeout) ? commandResult.Timeout // use the timeout reported back by the provider : CommandResult.MaximumTimeout; // use the defined maximum timeout functionContext.SetCustomStatus($"Waiting for command ({command.CommandId}) result on {commandCallback} for {commandTimeout}", log); commandResult = await functionContext .WaitForExternalEvent <ICommandResult>(command.CommandId.ToString(), commandTimeout, null) .ConfigureAwait(true); if (commandResult is null) { commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultActivity), (provider, commandMessage)) .ConfigureAwait(true); throw new TimeoutException($"Provider '{provider.Id}' ran into timeout ({commandTimeout})"); } } } catch (Exception exc) { functionContext.SetCustomStatus($"Sending command '{command.CommandId}' failed: {exc.Message}", log, exc); // ensure we always have a command result // to add our exception so we won't break // our command auditing in the finally block commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); // re-throw the exception to inform the // outer orchestration that some bad happened throw; } finally { if (!string.IsNullOrEmpty(commandCallback)) { functionContext.SetCustomStatus($"Invalidating callback url for command '{command.CommandId}'", log); await functionContext .CallActivityWithRetryAsync(nameof(CallbackInvalidateActivity), functionContext.InstanceId) .ConfigureAwait(true); } await functionContext .AuditAsync(provider, command, commandResult) .ConfigureAwait(true); await ProcessOutputAsync(functionContext, provider, command, commandResult) .ConfigureAwait(true); } return(commandResult); }
private static async Task <ICommandResult> ProcessCommandAsync(IDurableOrchestrationContext functionContext, ProviderDocument provider, IProviderCommand command, ICommandResult commandResult, ILogger log) { var commandMessage = default(ICommandMessage); var commandCallback = default(string); try { functionContext.SetCustomStatus($"Acquire callback url", log); commandCallback = await functionContext .CallActivityWithRetryAsync <string>(nameof(CallbackUrlGetActivity), (functionContext.InstanceId, command)) .ConfigureAwait(true); commandMessage = new ProviderCommandMessage(command, commandCallback); if (!(command is ProviderRegisterCommand || provider.Registered.HasValue)) { log.LogInformation($"Register provider {provider.Id} for command {command.CommandId}"); await functionContext .RegisterProviderAsync(provider, true) .ConfigureAwait(true); } if (!string.IsNullOrEmpty(command.ProjectId) && provider.PrincipalId.HasValue) { log.LogInformation($"Enable provider {provider.Id} for command {command.CommandId}"); await functionContext .CallActivityWithRetryAsync(nameof(ProjectResourcesAccessActivity), (command.ProjectId, provider.PrincipalId.Value)) .ConfigureAwait(true); } functionContext.SetCustomStatus($"Augmenting command", log); command = await AugmentCommandAsync(functionContext, provider, command) .ConfigureAwait(true); await functionContext .AuditAsync(command, commandResult, provider) .ConfigureAwait(true); try { functionContext.SetCustomStatus($"Sending command", log); commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandSendActivity), (provider, commandMessage)) .ConfigureAwait(true); } catch (RetryCanceledException) { commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, commandMessage)) .ConfigureAwait(true); } finally { await functionContext .AuditAsync(command, commandResult, provider) .ConfigureAwait(true); } if (commandResult.RuntimeStatus.IsActive()) { var commandTimeout = (commandResult.Timeout > TimeSpan.Zero && commandResult.Timeout < CommandResult.MaximumTimeout) ? commandResult.Timeout // use the timeout reported back by the provider : CommandResult.MaximumTimeout; // use the defined maximum timeout functionContext.SetCustomStatus($"Waiting for command result", log); commandResult = await functionContext .WaitForExternalEvent <ICommandResult>(command.CommandId.ToString(), commandTimeout, null) .ConfigureAwait(true); if (commandResult is null) { // provider ran into a timeout // lets give our provider a last // chance to return a command result commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(CommandResultFetchActivity), (provider, commandMessage)) .ConfigureAwait(true); if (commandResult.RuntimeStatus.IsActive()) { // the last change result still doesn't report a final runtime status // escalate the timeout by throwing an appropriate exception throw new TimeoutException($"Provider '{provider.Id}' ran into timeout ({commandTimeout})"); } } } } catch (Exception exc) { functionContext.SetCustomStatus($"Sending command failed: {exc.Message}", log, exc); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc.AsSerializable()); } finally { await functionContext .AuditAsync(command, commandResult, provider) .ConfigureAwait(true); await ProcessOutputAsync(functionContext, provider, command, commandResult) .ConfigureAwait(true); } return(commandResult); }
public static async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, [DurableClient] IDurableClient durableClient, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } if (durableClient is null) { throw new ArgumentNullException(nameof(durableClient)); } var commandMessage = functionContext.GetInput <ProviderCommandMessage>() ?? throw new ArgumentException("Command message is null", nameof(functionContext)); var command = commandMessage.Command ?? throw new ArgumentException("Command message does not contain a command", nameof(functionContext)); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); var commandOrchestrationName = await functionContext .CallActivityWithRetryAsync <string>(nameof(ProviderCommandDispatchActivity), command) .ConfigureAwait(true); var commandOrchestrationInstanceId = CommandHandler.GetCommandOrchestrationInstanceId(command); if (string.IsNullOrEmpty(commandOrchestrationName)) { commandLog .LogInformation($"Dispatching command '{command.GetType().FullName}' ({command.CommandId}) >>> FALLBACK ({commandOrchestrationInstanceId})"); commandResult = await functionContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(nameof(ProviderCommandFallbackOrchestration), commandOrchestrationInstanceId, command) .ConfigureAwait(true); } else { commandLog .LogInformation($"Dispatching command '{command.GetType().FullName}' ({commandMessage.CommandId}) >>> {commandOrchestrationName} ({commandOrchestrationInstanceId})"); commandResult = await functionContext .CallSubOrchestratorWithRetryAsync <ICommandResult>(commandOrchestrationName, commandOrchestrationInstanceId, command) .ConfigureAwait(true); } do { // there is a chance that the suborchestration used to agument the command result // doesn't reflect the final runtime status (completed / failed / canceled) because // of timing issues in the durable functions runtime. to void a none final runtime // status reported back to the orchestrator we loop / wait for this runtime status. commandResult = await functionContext .CallActivityWithRetryAsync <ICommandResult>(nameof(ProviderCommandResultAugmentActivity), (commandResult, commandOrchestrationInstanceId)) .ConfigureAwait(true); }while (commandResult.RuntimeStatus.IsActive()); } catch (Exception exc) { commandLog.LogError(exc, $"Processing command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}"); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { try { await functionContext .CallActivityWithRetryAsync(nameof(ProviderCommandResultSendActivity), (commandResult, commandMessage.CallbackUrl)) .ConfigureAwait(true); } catch (Exception exc) { commandLog.LogError(exc, $"Sending result for command '{command.GetType().FullName}' ({command.CommandId}) Failed >>> {exc.Message}"); commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); } finally { await functionContext .AuditAsync(command, commandResult) .ConfigureAwait(true); functionContext.SetOutput(commandResult); } } }