public static async Task <int> PeriodicTask( [OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log) { log = ctx.CreateReplaySafeLogger(log); var timesRun = ctx.GetInput <int>(); timesRun++; log.LogInformation($"Starting the PeriodicTask activity {ctx.InstanceId}, {timesRun}"); await ctx.CallActivityAsync("A_PeriodicActivity", timesRun); var nextRun = ctx.CurrentUtcDateTime.AddSeconds(30); await ctx.CreateTimer(nextRun, CancellationToken.None); ctx.ContinueAsNew(timesRun); return(timesRun); }
public async Task RunAsync( [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext, ILogger log) { ILogger replaySafeLogger = orchestrationContext.CreateReplaySafeLogger(log); TenantedFunctionData <CreateNotificationsRequest> request = orchestrationContext.GetInput <TenantedFunctionData <CreateNotificationsRequest> >(); try { await orchestrationContext.CallActivityAsync( nameof(StartLongRunningOperationActivity), (request.LongRunningOperationId !.Value, request.TenantId)); // Fan out to create each notification string[] correlationIds = new string[request.Payload.CorrelationIds.Length + 1]; request.Payload.CorrelationIds.CopyTo(correlationIds, 0); correlationIds[^ 1] = orchestrationContext.InstanceId;
public static async Task <List <string> > HelloCities( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { logger = context.CreateReplaySafeLogger(logger); logger.LogInformation("Starting '{name}' orchestration with ID = 'id'", context.Name, context.InstanceId); var outputs = new List <string> { await context.CallActivityAsync <string>(nameof(Common.SayHello), "Tokyo"), await context.CallActivityAsync <string>(nameof(Common.SayHello), "Seattle"), await context.CallActivityAsync <string>(nameof(Common.SayHello), "London"), await context.CallActivityAsync <string>(nameof(Common.SayHello), "Amsterdam"), await context.CallActivityAsync <string>(nameof(Common.SayHello), "Mumbai") }; logger.LogInformation("Finished '{name}' orchestration with ID = 'id'", context.Name, context.InstanceId); return(outputs); }
public async Task <string[]> Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var logger = context.CreateReplaySafeLogger(log); var accountIds = context.GetInput <string[]>(); logger.LogInformation($"Fan out account ids. {ObjectDumper.Dump(accountIds)}"); var gameIdTasks = new List <Task <string[]> >(); foreach (var accountId in accountIds) { var activityInput = (_lolChestConfig.Region, accountId); gameIdTasks.Add(context.CallActivityAsync <string[]>("GetGameIds", activityInput)); } var gameIdsOfSummoners = await Task.WhenAll(gameIdTasks); logger.LogInformation($"Retrieved game ids of all summoners. {ObjectDumper.Dump(gameIdsOfSummoners)}"); var allGameIds = gameIdsOfSummoners.SelectMany(x => x).Distinct(); var commonGameIds = new List <string>(); foreach (var gameId in allGameIds) { var isCommonGame = true; foreach (var gameIdsOfSummoner in gameIdsOfSummoners) { isCommonGame &= gameIdsOfSummoner.Contains(gameId); } if (isCommonGame) { commonGameIds.Add(gameId); } } logger.LogInformation($"Filtered common game ids. {ObjectDumper.Dump(commonGameIds)}"); return(commonGameIds.ToArray()); }
public static async Task TaskExecutionOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); var request = context.GetInput <TaskExecutionRequest>(); var taskHierarchy = request.Tasks; log.LogWarning("*** {eventName}: {taskId} on compute set {computeSetId}", "TasksStarting", taskHierarchy.Id, taskHierarchy.ComputeSetId); foreach (var taskItem in taskHierarchy.TaskItems) { var message = await context.CallActivityAsync <string>("ExecuteTask", taskItem); } //log.LogWarning("*** {eventName}: compute set {computeSetId}", "SignalTasksCompleted", taskHierarchy.ComputeSetId); var computeSet = context.CreateEntityProxy <IComputeSet>(new EntityId(nameof(ComputeSet), taskHierarchy.ComputeSetId)); await computeSet.SignalTasksCompleted(request); }
public static async Task PeriodicCrawlExecuter( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { logger = context.CreateReplaySafeLogger(logger); var dueTime = context.CurrentUtcDateTime.AddMinutes(15); dueTime = dueTime.AddSeconds(-dueTime.Second); var needCrawlContests = context.GetInput <List <string> >() ?? new List <string>(); var tasks = new Task <bool> [needCrawlContests.Count]; logger.LogInformation($"needCrawlContests : [{string.Join(", ", needCrawlContests)}]"); for (int i = 0; i < tasks.Length; i++) { logger.LogInformation($"start updating {needCrawlContests[i]}"); tasks[i] = context.CallActivityAsync <bool>("UpdateAPerfs", needCrawlContests[i]); } for (int i = 0; i < tasks.Length; i++) { await tasks[i]; } logger.LogInformation("completed"); for (int i = tasks.Length - 1; i >= 0; i--) { if (!tasks[i].Result) { needCrawlContests.RemoveAt(i); } } if (needCrawlContests.Count == 0) { return; } await context.CreateTimer(dueTime, CancellationToken.None); context.ContinueAsNew(needCrawlContests); }
public async Task <string[]> Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var logger = context.CreateReplaySafeLogger(log); logger.LogInformation("Fan out summoner names to retrieve account ids."); var accountIdTasks = new List <Task <string> >(); foreach (var summonerName in _lolChestConfig.SummonerNames) { var activityInput = (_lolChestConfig.Region, summonerName); accountIdTasks.Add(context.CallActivityAsync <string>("GetAccountId", activityInput)); } var accountIds = await Task.WhenAll(accountIdTasks); logger.LogInformation($"Retrieved account ids. {ObjectDumper.Dump(accountIds)}"); return(accountIds); }
public static async Task UpdateHutsOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); int startHutId = context.GetInput <int>(); log.LogInformation("Starting orchestrator with startHutId={startHutId}", startHutId); var tasks = new List <Task>(); // Fan-out. Every day we check 1/7 of all hut IDs in the range for (int i = startHutId; i <= MaxHutId; i = i + 7) { tasks.Add(context.CallActivityAsync(nameof(GetHutFromProviderActivity), i)); } // Fan in. Wait for all to be finished await Task.WhenAll(tasks); log.LogInformation("MaxHutId {MaxHutId} reached. Ending hut updating now.", MaxHutId); }
public async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var logger = context.CreateReplaySafeLogger(log); var gameIds = context.GetInput <string[]>(); logger.LogInformation($"Fan out game ids to store them in chest. {ObjectDumper.Dump(gameIds)}"); var addMatchToChestTasks = new List <Task>(); foreach (var gameId in gameIds) { var activityInput = (_lolChestConfig.Region, gameId); addMatchToChestTasks.Add(context.CallActivityAsync("AddMatchToChest", activityInput)); } await Task.WhenAll(addMatchToChestTasks); logger.LogInformation($"Game ids were added to the chest. {ObjectDumper.Dump(gameIds)}"); }
public async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext, ILogger log) { ILogger replaySafeLogger = orchestrationContext.CreateReplaySafeLogger(log); replaySafeLogger.LogDebug("Starting new TriggerInstancesExecutionOrchestrator instance"); WorkflowMessageEnvelope envelope = orchestrationContext.GetInputWithCustomSerializationSettings <WorkflowMessageEnvelope>( this.serializerSettingsProvider.Instance); int pageNumber = envelope.GetWorkflowInstancesPageNumber(); replaySafeLogger.LogDebug($"Processing trigger {envelope.Trigger.Id} against instances page {pageNumber}"); string[] instanceIds = await orchestrationContext.CallActivityWithCustomSerializationSettingsAsync <WorkflowMessageEnvelope, string[]>( nameof(GetWorkflowInstanceIdsActivity), envelope, this.serializerSettingsProvider.Instance); replaySafeLogger.LogDebug($"Retrieved {instanceIds.Length} instance Ids"); var tasks = new List <Task>(instanceIds.Length); foreach (string current in instanceIds) { envelope.SetWorkflowInstanceId(this.propertyBagFactory, current); tasks.Add(orchestrationContext.CallActivityWithCustomSerializationSettingsAsync( nameof(ProcessTriggerActivity), envelope, this.serializerSettingsProvider.Instance)); } await Task.WhenAll(tasks); replaySafeLogger.LogDebug($"Processing trigger {envelope.Trigger.Id} against instances page {pageNumber}"); }
public static async Task ProcessOrder( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); var orderId = await context.CallActivityAsync <Guid>(nameof(ProcessOrderActivity.CreateOrder), null); await context.CallActivityAsync(nameof(ProcessOrderActivity.UpdateOrderSetStatus), new UpdateOrder { OrderId = orderId, OrchestrationId = context.InstanceId, Status = OrderStatus.WaitForPayment.ToString() }); await context.CallActivityAsync(nameof(ProcessOrderActivity.StartPayment), new StartPayment { OrderId = orderId, OrchestrationId = context.InstanceId }); var paymentResult = await WaitForPaymentResultEvent(context, log); await context.CallActivityAsync( nameof(ProcessOrderActivity.UpdateOrderSetStatus), new UpdateOrder { OrderId = orderId, OrchestrationId = context.InstanceId, Status = paymentResult }); }
public static async Task <object> ProcessFileOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { string result1 = null; string result2 = null; string result3 = null; try { log = context.CreateReplaySafeLogger(log); var fileLocation = context.GetInput <string>(); result1 = await context.CallActivityAsync <string>("CleanData", fileLocation); result2 = await context.CallActivityAsync <string>("ApplyRules", result1); result3 = await context.CallActivityAsync <string>("ExtractData", result2); return(new { CleanData = result1, ApplyRules = result2, ExtractData = result3 }); } catch (Exception ex) { log.LogError($"Exception Occured: {ex.Message}"); await context.CallActivityAsync <string>("Cleanup", "temp data"); return(new { Error = "Failed to process file", Message = ex.Message }); } }
public static async Task <string> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); log.LogInformation("Someone entered meeting. Starting orchestration..."); var req = context.GetInput <RoomRequest>(); log.LogInformation($"Getting devices for room {req.RoomId}"); // Get all devices for the room DeviceIds devices = await context.CallActivityAsync <DeviceIds>("GetDevices", req.RoomId); var taskList = new List <Task>(); // Toggle all of the lights foreach (var lightId in devices.Lights) { taskList.Add( context.CallActivityAsync("ToggleLights", new LightToggleRequest(lightId, req.Toggle)) ); } // Connect room device IN PARALLEL taskList.Add( context.CallActivityAsync("ConnectRoom", new ConnectRoomRequest(devices.ConferencePhone, req.Toggle)) ); // Wait for all lights and room connected await Task.WhenAll(taskList); // Notify attendees the room is dialed in await context.CallActivityAsync("NotifyAttendees", req); return("Room Activated"); }
public async Task ReindexInstancesAsync( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { EnsureArg.IsNotNull(context, nameof(context)); logger = context.CreateReplaySafeLogger(logger); ReindexInput input = context.GetInput <ReindexInput>(); // The ID should be a GUID as generated by the trigger, but we'll assert here just to make sure! if (!context.HasInstanceGuid()) { return; } // Fetch the set of query tags that require re-indexing IReadOnlyList <ExtendedQueryTagStoreEntry> queryTags = await GetOperationQueryTagsAsync(context, input); logger.LogInformation( "Found {Count} extended query tag paths to re-index {{{TagPaths}}}.", queryTags.Count, string.Join(", ", queryTags.Select(x => x.Path))); List <int> queryTagKeys = queryTags.Select(x => x.Key).ToList(); if (queryTags.Count > 0) { IReadOnlyList <WatermarkRange> batches = await context.CallActivityWithRetryAsync <IReadOnlyList <WatermarkRange> >( nameof(GetInstanceBatchesV2Async), _options.ActivityRetryOptions, BatchCreationArguments.FromOptions(input.Completed?.Start - 1, _options)); if (batches.Count > 0) { // Note that batches are in reverse order because we start from the highest watermark var batchRange = new WatermarkRange(batches[^ 1].Start, batches[0].End);
public async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext orchestrationContext, ILogger log) { ILogger replaySafeLogger = orchestrationContext.CreateReplaySafeLogger(log); WorkflowMessageEnvelope envelope = orchestrationContext.GetInputWithCustomSerializationSettings <WorkflowMessageEnvelope>( this.serializerSettingsProvider.Instance); try { await orchestrationContext.CallActivityAsync( nameof(StartLongRunningOperationActivity), (envelope.OperationId, envelope.TenantId)); if (envelope.IsStartWorkflowRequest) { replaySafeLogger.LogDebug( $"Received new start workflow request with Id {envelope.StartWorkflowInstanceRequest.RequestId} for workflow {envelope.StartWorkflowInstanceRequest.WorkflowId}"); await orchestrationContext.CallActivityWithCustomSerializationSettingsAsync( nameof(CreateWorkflowActivity), envelope, this.serializerSettingsProvider.Instance); } else { replaySafeLogger.LogDebug($"Received new workflow trigger with Id {envelope.Trigger.Id}"); int count = await orchestrationContext.CallActivityWithCustomSerializationSettingsAsync <WorkflowMessageEnvelope, int>( nameof(GetWorkflowInstanceCountActivity), envelope, this.serializerSettingsProvider.Instance); int pages = (int)Math.Ceiling((decimal)count / 500); replaySafeLogger.LogDebug($"Found {count} instances that match. Split into {pages} pages for fan-out."); var tasks = new Task[pages]; for (int i = 0; i < pages; i++) { envelope.SetWorkflowInstancesPageNumber(this.propertyBagFactory, i); tasks[i] = orchestrationContext.CallSubOrchestratorWithCustomSerializationSettingsAsync( nameof(TriggerInstancesExecutionOrchestrator), envelope, this.serializerSettingsProvider.Instance); } await Task.WhenAll(tasks); replaySafeLogger.LogDebug("All sub-orchestrations complete"); } await orchestrationContext.CallActivityAsync( nameof(CompleteLongRunningOperationActivity), (envelope.OperationId, envelope.TenantId)); } catch (FunctionFailedException x) { replaySafeLogger.LogError($"Error during orchestration: {x}"); await orchestrationContext.CallActivityAsync( nameof(FailLongRunningOperationActivity), (envelope.OperationId, envelope.TenantId)); throw; } }
public static async Task RunOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } var command = functionContext.GetInput <ProviderProjectCreateCommand>(); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); using (log.BeginCommandScope(command)) { try { functionContext.SetCustomStatus("Deploy resources", commandLog); var deploymentOutput = await functionContext .CallDeploymentAsync(nameof(ProjectCreateActivity), command.Payload) .ConfigureAwait(true); if (deploymentOutput.TryGetValue("resourceId", out var resourceId)) { functionContext.SetCustomStatus("Updating user permissions", commandLog); await functionContext .CallActivityWithRetryAsync(nameof(ProjectUsersActivity), (command.Payload, resourceId?.ToString())) .ConfigureAwait(true); } commandResult.Result = new ProviderOutput { Properties = deploymentOutput.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToString()) }; } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); throw exc.AsSerializable(); } finally { var commandException = commandResult.Errors?.ToException(); if (commandException is null) { functionContext.SetCustomStatus($"Command succeeded", commandLog); } else { functionContext.SetCustomStatus($"Command failed: {commandException.Message}", commandLog, commandException); } functionContext.SetOutput(commandResult); } } }
public static async Task RunOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } var command = functionContext.GetInput <ProviderProjectCreateCommand>(); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); using (log.BeginCommandScope(command)) { try { await functionContext .EnsureAuthorizedAsync() .ConfigureAwait(true); await functionContext .CallOperationAsync(nameof(ProjectCreateActivity), command.Payload) .ConfigureAwait(true); await functionContext .CallActivityWithRetryAsync(nameof(ProjectInitializeActivity), command.Payload) .ConfigureAwait(true); await functionContext .CallSubOrchestratorAsync(nameof(ProjectSyncOrchestration), command.Payload) .ConfigureAwait(true); commandResult.Result = new ProviderOutput { Properties = new Dictionary <string, string>() }; } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); throw exc.AsSerializable(); } finally { var commandException = commandResult.Errors?.ToException(); if (commandException is null) { functionContext.SetCustomStatus($"Command succeeded", commandLog); } else { functionContext.SetCustomStatus($"Command failed: {commandException.Message}", commandLog, commandException); } functionContext.SetOutput(commandResult); } } }
public static async Task <object> ProcessVideo( [OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log) { log = ctx.CreateReplaySafeLogger(log); var videoLocation = ctx.GetInput <string>(); log.LogInformation("About to call transcode video activity"); string transcodedLocation = null; string thumbnailLocation = null; string withIntroLocation = null; string approvalResult = "Unknown"; try { ctx.SetCustomStatus("transcoding"); var transcodeResults = await ctx.CallSubOrchestratorAsync <VideoFileInfo[]>("O_TranscodeVideo", videoLocation); transcodedLocation = transcodeResults .OrderByDescending(r => r.BitRate) .Select(r => r.Location) .First(); ctx.SetCustomStatus("extracting thumbnail"); log.LogInformation("About to call extract thumbnail"); thumbnailLocation = await ctx.CallActivityAsync <string>("A_ExtractThumbnail", transcodedLocation); ctx.SetCustomStatus("prepending intro"); log.LogInformation("About to call prepend intro"); withIntroLocation = await ctx.CallActivityAsync <string>("A_PrependIntro", transcodedLocation); ctx.SetCustomStatus("sending approval request email"); await ctx.CallActivityAsync("A_SendApprovalRequestEmail", new ApprovalInfo() { OrchestrationId = ctx.InstanceId, VideoLocation = withIntroLocation }); ctx.SetCustomStatus("waiting for email response"); try { approvalResult = await ctx.WaitForExternalEvent <string>("ApprovalResult", TimeSpan.FromSeconds(30)); } catch (TimeoutException) { log.LogWarning("Timed out waiting for approval"); approvalResult = "Timed Out"; } if (approvalResult == "Approved") { ctx.SetCustomStatus("publishing video"); await ctx.CallActivityAsync("A_PublishVideo", withIntroLocation); } else { ctx.SetCustomStatus("rejecting video"); await ctx.CallActivityAsync("A_RejectVideo", withIntroLocation); } ctx.SetCustomStatus("finished"); } catch (Exception e) { log.LogError($"Caught an error from an activity: {e.Message}"); ctx.SetCustomStatus("error: cleaning up"); await ctx.CallActivityAsync <string>("A_Cleanup", new[] { transcodedLocation, thumbnailLocation, withIntroLocation }); ctx.SetCustomStatus("finished with error"); return(new { Error = "Failed to process uploaded video", Message = e.Message }); } return(new { Transcoded = transcodedLocation, Thumbnail = thumbnailLocation, WithIntro = withIntroLocation, ApprovalResult = approvalResult }); }
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 RunOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } (string operationActivityName, object operationActivityInput, string operationInstanceId) = functionContext.GetInput <(string, object, string)>(); var operationLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { if (string.IsNullOrEmpty(operationInstanceId)) { operationInstanceId = await functionContext .CallActivityWithRetryAsync <string>(operationActivityName, operationActivityInput) .ConfigureAwait(true); if (!string.IsNullOrEmpty(operationInstanceId)) { functionContext.ContinueAsNew((operationActivityName, operationActivityInput, operationInstanceId)); } } else { await functionContext .CreateTimer(functionContext.CurrentUtcDateTime.AddSeconds(10), CancellationToken.None) .ConfigureAwait(true); var status = await functionContext .CallActivityWithRetryAsync <OperationStatus>(nameof(OperationStatusActivity), operationInstanceId) .ConfigureAwait(true); if (status.IsProgressStatus()) { functionContext .ContinueAsNew((operationActivityName, operationActivityInput, operationInstanceId)); } else if (status.IsErrorStatus()) { var operationError = await functionContext .CallActivityWithRetryAsync <string>(nameof(OperationErrorActivity), operationInstanceId) .ConfigureAwait(false); if (!string.IsNullOrEmpty(operationError)) { throw new Exception(operationError); } } } } catch (Exception exc) { operationLog.LogError(exc, $"Orchestration '{nameof(OperationOrchestration)}' failed: {exc.Message}"); throw exc.AsSerializable(); } }
public static async Task RunOrchestration( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } var command = functionContext.GetInput <ProviderProjectCreateCommand>(); var commandResult = command.CreateResult(); var commandLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); using (log.BeginCommandScope(command)) { try { var deploymentOutput = await functionContext .CallDeploymentAsync(nameof(ProjectCreateActivity), command.Payload) .ConfigureAwait(true); commandResult.Result = new ProviderOutput { Properties = deploymentOutput.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToString()) }; var resources = await functionContext .CallActivityWithRetryAsync <IEnumerable <string> >(nameof(ProjectResourceListActivity), command.Payload) .ConfigureAwait(true); var tasks = new List <Task>(); tasks.AddRange(resources.Select(resource => functionContext.CallActivityWithRetryAsync(nameof(ProjectResourceRolesActivity), (command.Payload, resource)))); tasks.AddRange(resources.Select(resource => functionContext.CallActivityWithRetryAsync(nameof(ProjectResourceTagsActivity), (command.Payload, resource)))); await Task .WhenAll(tasks) .ConfigureAwait(true); } catch (Exception exc) { commandResult ??= command.CreateResult(); commandResult.Errors.Add(exc); throw exc.AsSerializable(); } finally { var commandException = commandResult.Errors?.ToException(); if (commandException is null) { functionContext.SetCustomStatus($"Command succeeded", commandLog); } else { functionContext.SetCustomStatus($"Command failed: {commandException.Message}", commandLog, commandException); } functionContext.SetOutput(commandResult); } } }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger log) { if (functionContext is null) { throw new ArgumentNullException(nameof(functionContext)); } (string deploymentOwnerInstanceId, string deploymentActivityName, object deploymentActivityInput, string deploymentResourceId, string deploymentOutputEventName) = functionContext.GetInput <(string, string, object, string, string)>(); var deploymentLog = functionContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); try { if (string.IsNullOrEmpty(deploymentResourceId)) { functionContext.SetCustomStatus($"Starting deployment using activity '{deploymentActivityName}'", deploymentLog); deploymentResourceId = await functionContext .CallActivityWithRetryAsync <string>(deploymentActivityName, deploymentActivityInput) .ConfigureAwait(true); if (!string.IsNullOrEmpty(deploymentResourceId)) { functionContext.SetCustomStatus($"Monitoring deployment '{deploymentResourceId}'", deploymentLog); functionContext.ContinueAsNew((deploymentOwnerInstanceId, deploymentActivityName, deploymentActivityInput, deploymentResourceId, deploymentOutputEventName)); } } else { await functionContext .CreateTimer(functionContext.CurrentUtcDateTime.AddSeconds(10), CancellationToken.None) .ConfigureAwait(true); var state = await functionContext .CallActivityWithRetryAsync <AzureDeploymentState>(nameof(AzureDeploymentStateActivity), deploymentResourceId) .ConfigureAwait(true); if (state.IsProgressState()) { functionContext .ContinueAsNew((deploymentOwnerInstanceId, deploymentActivityName, deploymentActivityInput, deploymentResourceId, deploymentOutputEventName)); } else if (state.IsErrorState()) { var errors = (await functionContext .CallActivityWithRetryAsync <IEnumerable <string> >(nameof(AzureDeploymentErrorsActivity), deploymentResourceId) .ConfigureAwait(true)) ?? Enumerable.Empty <string>(); foreach (var error in errors) { deploymentLog.LogError($"Deployment '{deploymentResourceId}' reported error: {error}"); } throw new AzureDeploymentException($"Deployment '{deploymentResourceId}' failed", deploymentResourceId, errors.ToArray()); } else { var output = await functionContext .GetDeploymentOutputAsync(deploymentResourceId) .ConfigureAwait(true); if (!string.IsNullOrEmpty(deploymentOutputEventName)) { await functionContext .RaiseEventAsync(deploymentOwnerInstanceId, deploymentOutputEventName, output) .ConfigureAwait(true); } functionContext.SetOutput(output); } } } catch (Exception exc) { deploymentLog.LogError(exc, $"Orchestration '{nameof(AzureDeploymentOrchestration)}' failed: {exc.Message}"); throw exc.AsSerializable(); } }
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); } } }
public async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); var appointment = context.GetInput <ClientAppointment>(); // Create confirmation code for appointment, send it to client, and return code as result var confirmationCode = await context .CallActivityAsync <ConfirmationCode>(nameof(AppointmentConfirmationNotificationActivity), appointment.PhoneNumber); using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); var confirmationExpiration = context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(20), cancellationTokenSource.Token); var appointmentConfirmation = context.WaitForExternalEvent <ConfirmationCode>("ConfirmAppointment"); var tasks = new List <Task> { appointmentConfirmation, confirmationExpiration }; // Give client X ammount of time to confirm appointment do { var result = await Task.WhenAny(tasks); if (result == appointmentConfirmation) { if (appointmentConfirmation.Result.Code == confirmationCode.Code) { log.LogInformation($"Appointment {appointment.AppointmentId} confirmed"); break; } } if (result == confirmationExpiration) { log.LogWarning($"Appointment {appointment.AppointmentId} failed to be confirmed"); cancellationTokenSource.Cancel(); } } while (!cancellationTokenSource.IsCancellationRequested); if (!cancellationTokenSource.IsCancellationRequested) { var appointmentCancelation = context.WaitForExternalEvent("CancelAppointment"); var appointmentReminder = context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(15), cancellationTokenSource.Token); // AFter this time expires, we assume that appointment is either currently happening or already happened var instanceExpiration = context.CreateTimer(context.CurrentUtcDateTime.AddSeconds(25), cancellationTokenSource.Token); tasks = new List <Task> { appointmentCancelation, appointmentReminder, instanceExpiration }; // Wait for appointment reminder/cancelation task completions do { var result = await Task.WhenAny(tasks); if (result == appointmentCancelation) { log.LogWarning($"Instance {context.InstanceId} canceling appointment"); cancellationTokenSource.Cancel(); } else if (result == appointmentReminder) { log.LogInformation("Sending appponintment reminder"); tasks.Remove(appointmentReminder); } else if (result == instanceExpiration) { cancellationTokenSource.Cancel(); } }while (!cancellationTokenSource.IsCancellationRequested); } }
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 functionLog = orchestrationContext.CreateReplaySafeLogger(log ?? NullLogger.Instance); var subscriptionId = orchestrationContext.GetInput <Guid>(); var resourceId = new AzureResourceIdentifier(subscriptionId); var initializationTimeout = TimeSpan.FromMinutes(5); try { var currentSubscriptionVersion = await orchestrationContext .GetSubscriptionVersionAsync(subscriptionId) .ConfigureAwait(true); if (currentSubscriptionVersion != TargetSubscriptionVersion) { using (await orchestrationContext.LockAzureResourceAsync(resourceId).ConfigureAwait(true)) { currentSubscriptionVersion = await orchestrationContext .GetSubscriptionVersionAsync(subscriptionId) .ConfigureAwait(true); if (currentSubscriptionVersion != TargetSubscriptionVersion) { // we have to offload the subscription initialization deployment to // an independant orchestration (StartDeploymentAsync) as we initiate // the deployment inside a critical section. this doesn't allow us // to run the deploy as a nested orchestration (CallDeploymentAsync). var deploymentOutputEventName = subscriptionId.ToString(); _ = await orchestrationContext .StartDeploymentAsync(nameof(ProjectSubscriptonInitializeActivity), subscriptionId, deploymentOutputEventName) .ConfigureAwait(true); await orchestrationContext .WaitForExternalEvent(deploymentOutputEventName, initializationTimeout) .ConfigureAwait(true); _ = await orchestrationContext .SetSubscriptionVersionAsync(subscriptionId, TargetSubscriptionVersion) .ConfigureAwait(true); } } } } catch (TimeoutException exc) { functionLog.LogError(exc, $"Failed to initialize subscription '{subscriptionId}' within a {initializationTimeout} timeout"); } catch (Exception exc) { functionLog.LogError(exc, $"Failed to initialize subscription '{subscriptionId}': {exc.Message}"); } }
public async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext functionContext, ILogger logger ) { log = functionContext.CreateReplaySafeLogger(logger); var data = functionContext.GetInput <string>(); //---------------------------------------------------------------- // Verify Email log.LogInformation("Calling SendEmailActivity"); functionContext.SetCustomStatus("SendingEmail"); try { data = await functionContext.CallActivityAsync <string>( "SendEmailActivity", data ); } catch (Exception) { log.LogInformation("SendEmailActivity failed; exiting."); return; } log.LogInformation("Waiting for Email verification"); functionContext.SetCustomStatus("AwaitingEmailVerification"); bool emailVerified = await functionContext.WaitForExternalEvent <bool>("EmailVerified"); log.LogInformation("Email verification received"); //---------------------------------------------------------------- // Verify SMS log.LogInformation("Calling SendSmsActivity"); functionContext.SetCustomStatus("SendingSMS"); try { data = await functionContext.CallActivityAsync <string>( "SendSmsActivity", data ); } catch (Exception) { log.LogInformation("SendSmsActivity failed; exiting."); return; } log.LogInformation("Waiting for SMS verification"); functionContext.SetCustomStatus("AwaitingSmsVerification"); bool smsVerified = await functionContext.WaitForExternalEvent <bool>("SmsVerified"); log.LogInformation("Sms verification received"); // ----------------------------------------------------- // Clean up var reg = UserRegistration.FromJson(data); new OrchestrationEntity().Delete("RegisterOrchestrator", functionContext.InstanceId); new RegistrationEntity().Delete("RegisterOrchestrator", functionContext.InstanceId); log.LogInformation("Orchestrator done"); }
public static async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { #region null checks if (context is null) { throw new ArgumentNullException(nameof(context)); } if (log is null) { throw new ArgumentNullException(nameof(log)); } #endregion log = context.CreateReplaySafeLogger(log); var catalogueDownloadInfo = context.GetInput <SaleFinderCatalogueDownloadInformation>(); var scanStateId = ICatalogueScanState.CreateId(new CatalogueScanStateKey(CatalogueType, catalogueDownloadInfo.Store, catalogueDownloadInfo.SaleId.ToString(CultureInfo.InvariantCulture))); var scanState = context.CreateEntityProxy <ICatalogueScanState>(scanStateId); using (await context.LockAsync(scanStateId).ConfigureAwait(true)) { #region Check and update the catalogue's scan state context.SetCustomStatus("CheckingState"); log.LogDebug($"Checking state - {scanStateId.EntityKey}"); var state = await scanState.GetState().ConfigureAwait(true); if (state != ScanState.NotStarted) { log.LogInformation($"Catalogue {scanStateId.EntityKey} already in state {state}, skipping scan."); context.SetCustomStatus("Skipped"); return; } await scanState.UpdateState(ScanState.InProgress).ConfigureAwait(true); #endregion #region Download catalogue context.SetCustomStatus("Downloading"); log.LogDebug($"Downloading - {scanStateId.EntityKey}"); var downloadedCatalogue = await context.CallActivityAsync <Catalogue>(SaleFinderFunctionNames.DownloadSaleFinderCatalogue, catalogueDownloadInfo).ConfigureAwait(true); #endregion #region Filter catalouge items context.SetCustomStatus("Filtering"); log.LogDebug($"Filtering - {scanStateId.EntityKey}"); var itemTasks = downloadedCatalogue.Items .Select(item => context.CallActivityAsync <CatalogueItem?>(CoreFunctionNames.FilterCatalogueItem, item)) .ToList(); await Task.WhenAll(itemTasks).ConfigureAwait(true); #endregion #region Send digest email context.SetCustomStatus("SendingDigestEmail"); log.LogDebug($"Sending digest email - {scanStateId.EntityKey}"); var filteredItems = itemTasks .Where(task => task.Result != null) .Select(task => task.Result !) .ToList(); if (filteredItems.Any()) { var filteredCatalogue = new Catalogue(downloadedCatalogue.Store, downloadedCatalogue.StartDate, downloadedCatalogue.EndDate, filteredItems); await context.CallActivityAsync(CoreFunctionNames.SendCatalogueDigestEmail, filteredCatalogue).ConfigureAwait(true); } else { log.LogInformation($"Catalogue {scanStateId.EntityKey} had no matching items, skipping digest email."); } #endregion #region Update catalogue's scan state context.SetCustomStatus("UpdatingState"); log.LogDebug($"Updating state - {scanStateId.EntityKey}"); await scanState.UpdateState(ScanState.Completed).ConfigureAwait(true); #endregion log.LogDebug($"Completed - {scanStateId.EntityKey}"); context.SetCustomStatus("Completed"); } }