public async Task SingleInstanceQuery() { object input = 42; DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.Sequence), input); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.Equal(10, (int)status.Output); IDurableClient client = await this.GetDurableClientAsync(); status = await client.GetStatusAsync(status.InstanceId); Assert.NotNull(status.Input); Assert.Equal(JTokenType.Integer, status.Input.Type); Assert.Equal(42, status.Input); Assert.Null(status.History); status = await client.GetStatusAsync(status.InstanceId, showHistory : true); Assert.NotNull(status.Input); Assert.Equal(JTokenType.Integer, status.Input.Type); Assert.Equal(42, status.Input); Assert.NotNull(status.History); Assert.NotEmpty(status.History); Assert.True(status.History.Count >= 12); // TODO: Check the content of the history for input/output fields }
public async Task SingleInstancePurge() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.NoOp)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); IDurableClient client = await this.GetDurableClientAsync(); PurgeHistoryResult result; // First purge gets the active instance result = await client.PurgeInstanceHistoryAsync(status.InstanceId); Assert.NotNull(result); Assert.Equal(1, result.InstancesDeleted); // Verify that it's gone DurableOrchestrationStatus statusAfterPurge = await client.GetStatusAsync(status.InstanceId); Assert.Null(statusAfterPurge); // Second purge should be a no-op result = await client.PurgeInstanceHistoryAsync(status.InstanceId); Assert.NotNull(result); Assert.Equal(0, result.InstancesDeleted); }
public OrchestratorModel(DurableOrchestrationStatus status) { Name = status.Name; InstanceId = status.InstanceId; RuntimeStatus = status.RuntimeStatus.ToString(); LastUpdatedTime = status.LastUpdatedTime.ToString("o"); }
public ExpandedOrchestrationStatus(DurableOrchestrationStatus that, Task <DurableOrchestrationStatus> detailsTask, Task <IEnumerable <HistoryEntity> > subOrchestrationsTask) { this.Name = that.Name; this.InstanceId = that.InstanceId; this.CreatedTime = that.CreatedTime; this.LastUpdatedTime = that.LastUpdatedTime; this.Input = that.Input; this.Output = that.Output; this.RuntimeStatus = that.RuntimeStatus; this.CustomStatus = that.CustomStatus; this.History = subOrchestrationsTask == null ? that.History : this.TryMatchingSubOrchestrations(that.History, subOrchestrationsTask); // Detecting whether it is an Orchestration or a Durable Entity var match = EntityIdRegex.Match(this.InstanceId); if (match.Success) { this.EntityType = EntityTypeEnum.DurableEntity; this.EntityId = new EntityId(match.Groups[1].Value, match.Groups[2].Value); } this._detailsTask = detailsTask; }
public async Task <IActionResult> GitHubPRCommentHook( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, [OrchestrationClient] IDurableOrchestrationClient starter, ILogger log) { string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); log.LogInformation(requestBody); // Parse the request var comment = JsonConvert.DeserializeObject <PRCommentCreated>(requestBody); // Start Orchestrator var commandName = comment.CommandName(); if (!string.IsNullOrEmpty(commandName)) { string instanceId = await starter.StartNewAsync(nameof(CreateWorkItemCommand), comment); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); DurableOrchestrationStatus status = await starter.GetStatusAsync(instanceId, false, false); return((ActionResult) new OkObjectResult(status)); } else { var status = new DurableOrchestrationStatus(); status.RuntimeStatus = OrchestrationRuntimeStatus.Completed; return((ActionResult) new OkObjectResult(status)); } }
public async Task CanOrchestrateEntities() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.OrchestrateCounterEntity)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.Equal(7, (int)status.Output); }
public async Task SupportsDeterministicGuid() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.DeterministicGuid)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.True((bool)status.Output); }
// TODO: Make this a built-in API public static async Task <DurableOrchestrationStatus> WaitForCompletionAsync( this IDurableOrchestrationClient client, string instanceId, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { DurableOrchestrationStatus status = await client.GetStatusAsync(instanceId); switch (status?.RuntimeStatus) { case OrchestrationRuntimeStatus.Canceled: case OrchestrationRuntimeStatus.Completed: case OrchestrationRuntimeStatus.Failed: case OrchestrationRuntimeStatus.Terminated: return(status); } await Task.Delay(TimeSpan.FromMilliseconds(500)); } cancellationToken.ThrowIfCancellationRequested(); // Code should never reach here return(null !); }
public async Task <DurableOrchestrationStatus> WaitForCompletionAsync( ITestOutputHelper output, bool showHistory = false, bool showHistoryOutput = false, TimeSpan?timeout = null) { if (timeout == null) { timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30); } Stopwatch sw = Stopwatch.StartNew(); do { output.WriteLine($"Waiting for instance {this.instanceId} to complete."); DurableOrchestrationStatus status = await this.GetStatusAsync(showHistory, showHistoryOutput); if (status?.RuntimeStatus == OrchestrationRuntimeStatus.Completed || status?.RuntimeStatus == OrchestrationRuntimeStatus.Failed || status?.RuntimeStatus == OrchestrationRuntimeStatus.Terminated) { output.WriteLine($"{status.Name} (ID = {status.InstanceId}) completed after ~{sw.ElapsedMilliseconds}ms. Status = {status.RuntimeStatus}. Output = {status.Output}."); return(status); } await Task.Delay(TimeSpan.FromSeconds(1)); }while (sw.Elapsed < timeout); throw new TimeoutException($"Durable function '{this.functionName}' with instance ID '{this.instanceId}' failed to complete."); }
public static void LogHistory(DurableOrchestrationStatus status, TextWriter writer, bool showExceptionDetails = false) { var history = status.History.ToObject <List <GenericHistoryEvent> >(); writer.WriteLine("Instance ID.....: " + status.InstanceId); writer.WriteLine("Runtime status..: " + Enum.GetName(typeof(OrchestrationRuntimeStatus), status.RuntimeStatus)); if (null != status.CustomStatus) { writer.WriteLine("Custom status...: " + status.CustomStatus.ToString().Take(100)); } writer.WriteLine("History ->"); foreach (var ev in history) { writer.WriteLine(" " + ev.Timestamp.ToLocalTime().ToString("HH:mm:ss.fff") + " " + $"[{ev.EventId}] " + Enum.GetName(typeof(EventType), ev.EventType) + (string.IsNullOrEmpty(ev.TaskScheduledId) ? "" : $" ({ev.TaskScheduledId})") + (string.IsNullOrEmpty(ev.Name) ? "" : $" [{ev.Name}]") + (string.IsNullOrEmpty(ev.Data) ? "" : $" ({ev.Data})")); if (string.IsNullOrEmpty(ev.Reason)) { continue; } writer.WriteLine(" " + ev.Reason); if (showExceptionDetails) { writer.WriteLine(" " + ev.Details); } } }
public async Task <IActionResult> TerminateInstance( [DurableClient] IDurableOrchestrationClient client, [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req) { string reason = req.Query["reason"]; var taskItem = await base.GetTaskItem(req); var taskId = _service.TaskId(taskItem); if (string.IsNullOrWhiteSpace(taskId)) { return(new OkObjectResult("orchestratorId missing")); } DurableOrchestrationStatus status = await client.GetStatusAsync(taskId); if (status.RuntimeStatus == OrchestrationRuntimeStatus.Completed) { return(new OkObjectResult("Already completed")); } if (status.RuntimeStatus == OrchestrationRuntimeStatus.Terminated) { return(new OkObjectResult("Already terminated")); } await client.TerminateAsync(taskId, reason); return(new OkObjectResult("Teminated")); }
public async Task CanRunActivitySequences() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.Sequence)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.Equal(10, (int)status.Output); }
public async Task RunActivity_RetriesForCompleted( [Values(OrchestrationRuntimeStatus.Canceled, OrchestrationRuntimeStatus.Failed, OrchestrationRuntimeStatus.Terminated, OrchestrationRuntimeStatus.Completed)] OrchestrationRuntimeStatus runtimeStatus) { var data = CreatePushData("test", "test"); var instanceId = CreateInstanceId(data); _mockDurableClient.CreateCheckStatusResponse(Arg.Any <HttpRequestMessage>(), instanceId).Returns(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(string.Empty) }); var status = new DurableOrchestrationStatus { RuntimeStatus = runtimeStatus }; _mockDurableClient.GetStatusAsync(instanceId).Returns(Task.FromResult(status)); var request = new HttpRequestMessage() { Content = new StringContent(JsonConvert.SerializeObject(data), System.Text.Encoding.UTF8, "application/json"), }; var result = await RepositoryValidatorEndpoint.RepositoryValidatorTrigger(request, _mockDurableClient, Substitute.For <ILogger>()); Assert.AreEqual(result.StatusCode, HttpStatusCode.OK); await _mockDurableClient.Received().StartNewAsync(Arg.Any <string>(), Arg.Any <string>(), Arg.Any <object>()); }
public ExpandedOrchestrationStatus(DurableOrchestrationStatus that, Task <DurableOrchestrationStatus> detailsTask, HashSet <string> hiddenColumns = null) { this.Name = that.Name; this.InstanceId = that.InstanceId; this.CreatedTime = that.CreatedTime; this.LastUpdatedTime = that.LastUpdatedTime; this.RuntimeStatus = that.RuntimeStatus; this.Input = (hiddenColumns != null && hiddenColumns.Contains("input")) ? null : that.Input; this.Output = (hiddenColumns != null && hiddenColumns.Contains("output")) ? null : that.Output; this.CustomStatus = (hiddenColumns != null && hiddenColumns.Contains("customStatus")) ? null : that.CustomStatus; // Detecting whether it is an Orchestration or a Durable Entity var match = EntityIdRegex.Match(this.InstanceId); if (match.Success) { this.EntityType = EntityTypeEnum.DurableEntity; this.EntityId = new EntityId(match.Groups[1].Value, match.Groups[2].Value); } this._detailsTask = detailsTask; }
public async Task CanInvokeSubOrchestration() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.SubOrchestrationTest)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.Equal("done", status.Output); }
public async Task <DurableOrchestrationStatus> WaitForStartupAsync(ITestOutputHelper output, TimeSpan?timeout = null) { if (timeout == null) { // We wait up to 30 seconds for things to start. It shouldn't normally take this // long, but for whatever reason a small minority of tests don't acquire all // partitions in less than 10 seconds. timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30); } Stopwatch sw = Stopwatch.StartNew(); do { output.WriteLine($"Waiting for instance {this.instanceId} to start."); DurableOrchestrationStatus status = await this.GetStatusAsync(); if (status != null && status.RuntimeStatus != OrchestrationRuntimeStatus.Pending) { output.WriteLine($"{status.Name} (ID = {status.InstanceId}) started successfully after ~{sw.ElapsedMilliseconds}ms. Status = {status.RuntimeStatus}."); return(status); } await Task.Delay(TimeSpan.FromSeconds(1)); }while (sw.Elapsed < timeout); throw new TimeoutException($"Durable function '{this.functionName}' with instance ID '{this.instanceId}' failed to start."); }
private static bool IsInstanceOccupied(DurableOrchestrationStatus instanceStatus) { return(instanceStatus != null && (instanceStatus.RuntimeStatus == OrchestrationRuntimeStatus.Running || instanceStatus.RuntimeStatus == OrchestrationRuntimeStatus.Pending || instanceStatus.RuntimeStatus == OrchestrationRuntimeStatus.ContinuedAsNew)); }
public async Task StartTimeTriggeredTasks( [TimerTrigger("%SCHEDULER_INTERVAL%")] TimerInfo timer, [DurableClient] IDurableOrchestrationClient client, ILogger log) { var orchestratorInstanceId = ((int)TaskTypeEnum.DanDas).ToString(); DurableOrchestrationStatus status = await client.GetStatusAsync(orchestratorInstanceId); // avoid runtime error, from staring the same orchestration more than once. if (status != null && status.RuntimeStatus == OrchestrationRuntimeStatus.Running) { log.LogInformation($"Timer trigger already started : {DateTime.Now}"); return; } var curstomerIds = _service.GetTimeScheduleredCustomers(TaskTypeEnum.DanDas); var orchestratorFunction = (curstomerIds.Count <= 1) ? nameof(StartOrchestrator) : nameof(StartOrchestratorFanInOut); //string instanceId = await client.StartNewAsync<string>(nameof(StartOrchestrator), orchestratorInstanceId, "-1"); //log.LogInformation($"Started orchestration with ID = '{instanceId}'."); }
public static async Task <IActionResult> Status( [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req, [OrchestrationClient] DurableOrchestrationClient orchestrationClient, ILogger log) { var orchestrationInstanceId = req.Query["id"]; if (string.IsNullOrWhiteSpace(orchestrationInstanceId)) { return(new NotFoundResult()); } // Get the status for the passed in instanceId DurableOrchestrationStatus status = await orchestrationClient.GetStatusAsync(orchestrationInstanceId); if (status is null) { return(new NotFoundResult()); } var shortStatus = new { currentStatus = status.RuntimeStatus.ToString(), result = status.Output }; return(new OkObjectResult(shortStatus)); // We could also expand this and check status.RuntimeStatus and for example return a 202 if processing is still underway }
public static DurableOrchestrationStatus UpdateDurableOrchestrationStatus([ActivityTrigger] DurableActivityContext ctx) { DurableOrchestrationStatus durableOrchestrationStatus = ctx.GetInput <DurableOrchestrationStatus>(); durableOrchestrationStatus.RuntimeStatus = OrchestrationRuntimeStatus.Completed; durableOrchestrationStatus.CreatedTime = DateTime.UtcNow; durableOrchestrationStatus.LastUpdatedTime = DateTime.UtcNow.AddSeconds(5); return(durableOrchestrationStatus); }
public static OperationType GetOperationType(this DurableOrchestrationStatus durableOrchestrationStatus) { EnsureArg.IsNotNull(durableOrchestrationStatus, nameof(durableOrchestrationStatus)); return(durableOrchestrationStatus.Name != null && durableOrchestrationStatus.Name.StartsWith(FunctionNames.ReindexInstances, StringComparison.OrdinalIgnoreCase) ? OperationType.Reindex : OperationType.Unknown); }
public async Task ProcessSingleMessageFailTest() { var messageDetails = new MessageInformation { Body = Encoding.UTF8.GetBytes(GetMessageBody()), LockToken = Guid.NewGuid().ToString(), SessionId = "dc04c21f-091a-44a9-a661-9211dd9ccf35", MessageId = Guid.NewGuid().ToString() }; var output = new GroupMembershipMessageResponse { CompletedGroupMembershipMessages = new List <GroupMembershipMessage> { new GroupMembershipMessage { LockToken = messageDetails.LockToken } }, ShouldCompleteMessage = true }; var status = new DurableOrchestrationStatus { RuntimeStatus = OrchestrationRuntimeStatus.Running, Output = JToken.FromObject(output) }; _durableClientMock .Setup(x => x.StartNewAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <GraphUpdaterFunctionRequest>())) .ReturnsAsync(_instanceId); var attempt = 1; _durableClientMock .Setup(x => x.GetStatusAsync(It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <bool>(), It.IsAny <bool>())) .Callback(() => { if (attempt > 1) { status.RuntimeStatus = OrchestrationRuntimeStatus.Terminated; } attempt++; }) .ReturnsAsync(status); _messageService.Setup(x => x.GetMessageProperties(It.IsAny <Message>())).Returns(messageDetails); var starterFunction = new StarterFunction(_loggerMock, _messageService.Object, _configuration.Object); await starterFunction.RunAsync(new Message(), _durableClientMock.Object, _messageSessionMock.Object); _messageSessionMock.Verify(mock => mock.CompleteAsync(It.IsAny <IEnumerable <string> >()), Times.Never()); _messageSessionMock.Verify(mock => mock.CloseAsync(), Times.Never()); Assert.IsNotNull(_loggerMock.MessagesLogged.Single(x => x.Message.Contains("Error: Status of instance"))); Assert.IsNotNull(_loggerMock.MessagesLogged.Single(x => x.Message.Contains("function complete"))); }
public async Task CanRunFanOutFanIn() { DurableOrchestrationStatus status = await this.RunOrchestrationAsync(nameof(Functions.FanOutFanIn)); Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus); Assert.Equal( expected: @"[""9"",""8"",""7"",""6"",""5"",""4"",""3"",""2"",""1"",""0""]", actual: status.Output?.ToString(Formatting.None)); }
public static async Task <DurableOrchestrationStatus> GetDurableOrchestrationStatus([OrchestrationTrigger] IDurableOrchestrationContext ctx) { DurableOrchestrationStatus durableOrchestrationStatus = ctx.GetInput <DurableOrchestrationStatus>(); DurableOrchestrationStatus result = await ctx.CallActivityAsync <DurableOrchestrationStatus>( nameof(TestActivities.UpdateDurableOrchestrationStatus), durableOrchestrationStatus); return(result); }
public static bool IsFinalRuntimeStatus(this DurableOrchestrationStatus status) { if (status is null) { throw new ArgumentNullException(nameof(status)); } return(FinalRuntimeStatus.Contains((int)status.RuntimeStatus)); }
internal static ICommandResult CreateResult(this ICommand command, DurableOrchestrationStatus orchestrationStatus) { if (orchestrationStatus is null) { throw new ArgumentNullException(nameof(orchestrationStatus)); } var result = (orchestrationStatus.Output?.HasValues ?? false) ? orchestrationStatus.Output.ToObject <ICommandResult>() : command.CreateResult(); return(result.ApplyStatus(orchestrationStatus)); }
private void AssertHistoryEventOrder(DurableOrchestrationStatus status, params EventType[] eventOrder) { TestUtil.LogHistory(status, Console.Out); var history = status.History.ToObject <List <GenericHistoryEvent> >(); for (int i = 0; i < eventOrder.Length; i++) { Assert.AreEqual(eventOrder[i], history[i].EventType); } }
public async Task MultiInstancePurge() { DateTime startTime = SharedTestHelpers.GetCurrentDatabaseTimeUtc(); DurableOrchestrationStatus instance1 = await this.RunOrchestrationAsync(nameof(Functions.NoOp)); DurableOrchestrationStatus instance2 = await this.RunOrchestrationAsync(nameof(Functions.NoOp)); // Extra delay to account for test flakiness in the GitHub CI (TODO: Why is this necessary?) await Task.Delay(TimeSpan.FromSeconds(1)); DateTime midTime = SharedTestHelpers.GetCurrentDatabaseTimeUtc(); DurableOrchestrationStatus instance3 = await this.RunOrchestrationAsync(nameof(Functions.NoOp)); DurableOrchestrationStatus instance4 = await this.RunOrchestrationAsync(nameof(Functions.NoOp)); // Extra delay to account for test flakiness in the GitHub CI (TODO: Why is this necessary?) await Task.Delay(TimeSpan.FromSeconds(1)); DateTime endTime = SharedTestHelpers.GetCurrentDatabaseTimeUtc(); IDurableClient client = await this.GetDurableClientAsync(); PurgeHistoryResult purgeResult; // First attempt should delete nothing because it's out of range purgeResult = await client.PurgeInstanceHistoryAsync(endTime, null, null); Assert.NotNull(purgeResult); Assert.Equal(0, purgeResult.InstancesDeleted); // Purging from the test's mid-point should purge instance3 and instance4 purgeResult = await client.PurgeInstanceHistoryAsync(midTime, null, null); Assert.NotNull(purgeResult); Assert.Equal(2, purgeResult.InstancesDeleted); Assert.NotNull(await client.GetStatusAsync(instance1.InstanceId)); Assert.NotNull(await client.GetStatusAsync(instance2.InstanceId)); Assert.Null(await client.GetStatusAsync(instance3.InstanceId)); Assert.Null(await client.GetStatusAsync(instance4.InstanceId)); // Purging from the test start time should purge instance1 and instance2 purgeResult = await client.PurgeInstanceHistoryAsync(startTime, null, null); Assert.Equal(2, purgeResult.InstancesDeleted); Assert.Null(await client.GetStatusAsync(instance1.InstanceId)); Assert.Null(await client.GetStatusAsync(instance2.InstanceId)); // Purging again should be a no-op purgeResult = await client.PurgeInstanceHistoryAsync(startTime, null, null); Assert.Equal(0, purgeResult.InstancesDeleted); }
private static OperationProgress GetOperationProgress(OperationType type, DurableOrchestrationStatus status) { switch (type) { case OperationType.Reindex: ReindexInput reindexInput = status.Input?.ToObject <ReindexInput>() ?? new ReindexInput(); return(reindexInput.GetProgress()); default: return(new OperationProgress()); } }
protected async Task <DurableOrchestrationStatus> RunOrchestrationAsync(string name) { IDurableClient client = await this.GetDurableClientAsync(); string instanceId = await client.StartNewAsync(name); TimeSpan timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(10); DurableOrchestrationStatus status = await client.WaitForCompletionAsync(instanceId, timeout); Assert.NotNull(status); return(status); }