/// <summary> /// Purges orchestration instance state and history for orchestrations older than the specified threshold time. /// </summary> /// <param name="thresholdDateTimeUtc">Threshold date time in UTC</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> /// <returns></returns> public async Task PurgeOrchestrationInstanceHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { ThrowIfInstanceStoreNotConfigured(); TableContinuationToken continuationToken = null; TraceHelper.Trace(TraceEventType.Information, () => "Purging orchestration instances before: " + thresholdDateTimeUtc + ", Type: " + timeRangeFilterType); int purgeCount = 0; do { TableQuerySegment <OrchestrationStateEntity> resultSegment = (await tableClient.QueryOrchestrationStatesSegmentedAsync( new OrchestrationStateQuery() .AddTimeRangeFilter(DateTime.MinValue, thresholdDateTimeUtc, timeRangeFilterType), continuationToken, 100) .ConfigureAwait(false)); continuationToken = resultSegment.ContinuationToken; if (resultSegment.Results != null) { await PurgeOrchestrationHistorySegmentAsync(resultSegment).ConfigureAwait(false); purgeCount += resultSegment.Results.Count; } } while (continuationToken != null); TraceHelper.Trace(TraceEventType.Information, () => "Purged " + purgeCount + " orchestration histories"); }
public Task PurgeOrchestrationHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { if (timeRangeFilterType != OrchestrationStateTimeRangeFilterType.OrchestrationCompletedTimeFilter) { throw new NotSupportedException("Purging is supported only for Orchestration completed time filter."); } return(this.instanceStore.PurgeOrchestrationHistoryEventsAsync(thresholdDateTimeUtc)); }
public override async Task PurgeOrchestrationHistoryAsync( DateTime maxThresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { using SqlConnection connection = await this.GetAndOpenConnectionAsync(); using SqlCommand command = this.GetSprocCommand(connection, "dt.PurgeInstanceStateByTime"); command.Parameters.Add("@ThresholdTime", SqlDbType.DateTime2).Value = maxThresholdDateTimeUtc; command.Parameters.Add("@FilterType", SqlDbType.TinyInt).Value = (int)timeRangeFilterType; await SqlUtils.ExecuteNonQueryAsync(command, this.traceHelper); }
/// <summary> /// Adds a time range filter on the returned orchestrations /// </summary> /// <param name="startTime">Start of the time range to filter by</param> /// <param name="endTime">End of the time range to filter by</param> /// <param name="filterType">Type of orchestration timestamp to apply filter on</param> /// <returns></returns> public OrchestrationStateQuery AddTimeRangeFilter(DateTime startTime, DateTime endTime, OrchestrationStateTimeRangeFilterType filterType) { if (FilterMap.ContainsKey(typeof(OrchestrationStateTimeRangeFilter))) { throw new ArgumentException("Cannot add more than one time range filters"); } FilterMap.Add(typeof(OrchestrationStateTimeRangeFilter), new OrchestrationStateTimeRangeFilter { StartTime = startTime, EndTime = endTime, FilterType = filterType }); return(this); }
public override Task PurgeOrchestrationHistoryAsync( OrchestrationDbContext dbContext, DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { var executions = timeRangeFilterType switch { OrchestrationStateTimeRangeFilterType.OrchestrationCreatedTimeFilter => dbContext.Executions.Where(e => e.CreatedTime < thresholdDateTimeUtc).ToArray(), OrchestrationStateTimeRangeFilterType.OrchestrationLastUpdatedTimeFilter => dbContext.Executions.Where(e => e.LastUpdatedTime < thresholdDateTimeUtc).ToArray(), OrchestrationStateTimeRangeFilterType.OrchestrationCompletedTimeFilter => dbContext.Executions.Where(e => e.CompletedTime < thresholdDateTimeUtc).ToArray(), _ => throw new NotImplementedException() }; dbContext.Executions.RemoveRange(executions); dbContext.SaveChanges(); return(Task.CompletedTask); }
public override async Task PurgeOrchestrationHistoryAsync( OrchestrationDbContext dbContext, DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { switch (timeRangeFilterType) { case OrchestrationStateTimeRangeFilterType.OrchestrationCreatedTimeFilter: await dbContext.Database.ExecuteSqlInterpolatedAsync($"DELETE FROM Executions WHERE CreatedTime < {thresholdDateTimeUtc}"); break; case OrchestrationStateTimeRangeFilterType.OrchestrationLastUpdatedTimeFilter: await dbContext.Database.ExecuteSqlInterpolatedAsync($"DELETE FROM Executions WHERE LastUpdatedTime < {thresholdDateTimeUtc}"); break; case OrchestrationStateTimeRangeFilterType.OrchestrationCompletedTimeFilter: await dbContext.Database.ExecuteSqlInterpolatedAsync($"DELETE FROM Executions WHERE CompletedTime < {thresholdDateTimeUtc}"); break; } }
/// <summary> /// Purges history from storage for given time range /// </summary> /// <param name="thresholdDateTimeUtc">The datetime in UTC to use as the threshold for purging history</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> /// <returns>The number of history events purged.</returns> public async Task <int> PurgeOrchestrationHistoryEventsAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { TableContinuationToken continuationToken = null; int purgeCount = 0; do { TableQuerySegment <AzureTableOrchestrationStateEntity> resultSegment = (await tableClient.QueryOrchestrationStatesSegmentedAsync( new OrchestrationStateQuery() .AddTimeRangeFilter(DateTimeUtils.MinDateTime, thresholdDateTimeUtc, timeRangeFilterType), continuationToken, 100) .ConfigureAwait(false)); continuationToken = resultSegment.ContinuationToken; if (resultSegment.Results != null) { await PurgeOrchestrationHistorySegmentAsync(resultSegment).ConfigureAwait(false); purgeCount += resultSegment.Results.Count; } } while (continuationToken != null); return(purgeCount); }
/// <inheritdoc /> public Task PurgeOrchestrationHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { throw new NotImplementedException(); }
public async Task PurgesInstancesByStatus(OrchestrationStateTimeRangeFilterType filterType) { DateTime startTime = DateTime.UtcNow; var events = new ConcurrentDictionary <string, TaskCompletionSource <bool> >(); // Waits for an external event and then either completes or fails depending on that event IReadOnlyList <TestInstance <string> > instances = await this.testService.RunOrchestrations( count : 30, // ideally some multiple of 3 instanceIdGenerator : i => $"InstanceToPurge_{i:00}", inputGenerator : i => $"Hello, world {i}", orchestrationName : "SimpleDelay", version : string.Empty, implementation : async(ctx, input) => { var tcs = new TaskCompletionSource <bool>(); events[ctx.OrchestrationInstance.InstanceId] = tcs; bool shouldFail = await tcs.Task; if (shouldFail) { throw new Exception("Kah-BOOOOOM!!!"); } return(shouldFail); }, onEvent : (ctx, name, value) => { events[ctx.OrchestrationInstance.InstanceId].SetResult(bool.Parse(value)); }); await Task.WhenAll(instances.Select(instance => instance.WaitForStart())); // Try to purge the instance and check that it still exists await this.testService.PurgeAsync(startTime, filterType); foreach (TestInstance <string> instance in instances) { OrchestrationState runningState = await instance.GetStateAsync(); Assert.Equal(OrchestrationStatus.Running, runningState.OrchestrationStatus); } TimeSpan timeout = TimeSpan.FromSeconds(30); // We want to test a mix of completed, failed, and terminated instances to make sure they are all handled correctly. var tasks = new List <Task>(); for (int i = 0; i < instances.Count; i++) { int index = i; tasks.Add(Task.Run(async() => { TestInstance <string> instance = instances[index]; if (index % 3 == 0) { // Complete the instance await instance.RaiseEventAsync("Action", false); await instance.WaitForCompletion(timeout, OrchestrationStatus.Completed); } else if (index % 3 == 1) { // Fail the instance await instance.RaiseEventAsync("Action", true); await instance.WaitForCompletion(timeout, OrchestrationStatus.Failed); } else { // Terminate the instance await instance.TerminateAsync("Terminated!"); await instance.WaitForCompletion(timeout, OrchestrationStatus.Terminated); } })); } // Wait for all instances to transition into their final state await Task.WhenAll(tasks); // This time-based purge should remove all the instances await this.testService.PurgeAsync(startTime, filterType); foreach (TestInstance <string> instance in instances) { OrchestrationState purgedState = await instance.GetStateAsync(); Assert.Null(purgedState); } // One more purge, just to make sure there are no failures when there is nothing left to purge await this.testService.PurgeAsync(startTime, filterType); }
/// <summary> /// Purges orchestration instance state and history for orchestrations older than the specified threshold time. /// </summary> /// <param name="thresholdDateTimeUtc">Threshold date time in UTC</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> /// <returns></returns> /// <exception cref="InvalidOperationException">Thrown if instance store not configured</exception> public Task PurgeOrchestrationInstanceHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { return(this.serviceClient.PurgeOrchestrationHistoryAsync(thresholdDateTimeUtc, timeRangeFilterType)); }
/// <inheritdoc /> public override Task PurgeHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { return(this.instanceStore.PurgeOrchestrationHistoryEventsAsync(thresholdDateTimeUtc, timeRangeFilterType)); }
/// <summary> /// Purges orchestration instance state and history for orchestrations older than the specified threshold time. /// Also purges the blob storage. /// </summary> /// <param name="thresholdDateTimeUtc">Threshold date time in UTC</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> public async Task PurgeOrchestrationHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { List <Task <HttpResponseMessage> > allTasks = new List <Task <HttpResponseMessage> >(); foreach (var endpoint in await this.GetAllEndpointsAsync(CancellationToken.None)) { var uri = $"{endpoint.ToString()}/{GetHistoryFragment()}"; var task = this.HttpClient.PostAsJsonAsync(uri, new PurgeOrchestrationHistoryParameters { ThresholdDateTimeUtc = thresholdDateTimeUtc, TimeRangeFilterType = timeRangeFilterType }); allTasks.Add(task); } var responses = await Task.WhenAll(allTasks.ToArray()); foreach (var response in responses) { response.EnsureSuccessStatusCode(); } }
public Task PurgeAsync(DateTime minimumThreshold, OrchestrationStateTimeRangeFilterType filterType) { return(this.client.PurgeOrchestrationInstanceHistoryAsync( minimumThreshold, filterType)); }
/// <summary> /// Purges history from storage for given time range /// </summary> /// <param name="thresholdDateTimeUtc">The datetime in UTC to use as the threshold for purging history</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> /// <returns>The number of history events purged.</returns> public async Task <int> PurgeOrchestrationHistoryEventsAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { var purgeCount = await Client.PurgeOrchestrationInstanceHistoryAsync(thresholdDateTimeUtc, timeRangeFilterType); return(purgeCount); }
/// <inheritdoc /> public override Task PurgeHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { throw new NotSupportedException(); }
public async Task PurgeOrchestrationHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { var request = new PurgeOrchestrationHistoryRequest { ThresholdDateTimeUtc = ToTimestamp(thresholdDateTimeUtc), TimeRangeFilterType = (OrchestrationTimeFilterType)timeRangeFilterType }; await _client.PurgeOrchestrationHistoryAsync(request); }
/// <summary> /// Purges orchestration instance state and history for orchestrations older than the specified threshold time. /// </summary> /// <param name="thresholdDateTimeUtc">Threshold date time in UTC</param> /// <param name="timeRangeFilterType">What to compare the threshold date time against</param> /// <returns></returns> public async Task PurgeOrchestrationInstanceHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { ThrowIfInstanceStoreNotConfigured(); TableContinuationToken continuationToken = null; TraceHelper.Trace(TraceEventType.Information, () => "Purging orchestration instances before: " + thresholdDateTimeUtc + ", Type: " + timeRangeFilterType); int purgeCount = 0; do { TableQuerySegment<OrchestrationStateEntity> resultSegment = (await tableClient.QueryOrchestrationStatesSegmentedAsync( new OrchestrationStateQuery() .AddTimeRangeFilter(DateTime.MinValue, thresholdDateTimeUtc, timeRangeFilterType), continuationToken, 100) .ConfigureAwait(false)); continuationToken = resultSegment.ContinuationToken; if (resultSegment.Results != null) { await PurgeOrchestrationHistorySegmentAsync(resultSegment).ConfigureAwait(false); purgeCount += resultSegment.Results.Count; } } while (continuationToken != null); TraceHelper.Trace(TraceEventType.Information, () => "Purged " + purgeCount + " orchestration histories"); }
public async Task PurgeOrchestrationHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { using (var dbContext = _dbContextFactory()) { await _dbContextExtensions.PurgeOrchestrationHistoryAsync(dbContext, thresholdDateTimeUtc, timeRangeFilterType); } }
public abstract Task PurgeOrchestrationHistoryAsync(OrchestrationDbContext dbContext, DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType);
/// <summary> /// Adds a time range filter on the returned orchestrations /// </summary> /// <param name="startTime">Start of the time range to filter by</param> /// <param name="endTime">End of the time range to filter by</param> /// <param name="filterType">Type of orchestration timestamp to apply filter on</param> /// <returns></returns> public OrchestrationStateQuery AddTimeRangeFilter(DateTime startTime, DateTime endTime, OrchestrationStateTimeRangeFilterType filterType) { if (FilterMap.ContainsKey(typeof (OrchestrationStateTimeRangeFilter))) { throw new ArgumentException("Cannot add more than one time range filters"); } FilterMap.Add(typeof (OrchestrationStateTimeRangeFilter), new OrchestrationStateTimeRangeFilter { StartTime = startTime, EndTime = endTime, FilterType = filterType }); return this; }
/// <inheritdoc /> public abstract Task PurgeHistoryAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType);
/// <inheritdoc /> public async Task <int> PurgeOrchestrationHistoryEventsAsync(DateTime thresholdDateTimeUtc, OrchestrationStateTimeRangeFilterType timeRangeFilterType) { var deleteStatement = $@"DELETE h FROM {settings.WorkItemTableName} h JOIN {settings.OrchestrationStateTableName} e ON e.InstanceId = h.InstanceId AND e.ExecutionId = h.ExecutionId "; switch (timeRangeFilterType) { case OrchestrationStateTimeRangeFilterType.OrchestrationCompletedTimeFilter: deleteStatement += "WHERE e.CompletedTime <= @thresholdDateTimeUtc"; break; case OrchestrationStateTimeRangeFilterType.OrchestrationCreatedTimeFilter: deleteStatement += "WHERE e.CreatedTime <= @thresholdDateTimeUtc"; break; case OrchestrationStateTimeRangeFilterType.OrchestrationLastUpdatedTimeFilter: deleteStatement += "WHERE e.LastUpdatedTime <= @thresholdDateTimeUtc"; break; default: throw new ArgumentOutOfRangeException($"Unknown {nameof(timeRangeFilterType)} value: {timeRangeFilterType}"); } using (var connection = await settings.GetDatabaseConnection()) using (var command = connection.CreateCommand()) { command.AddParameter("thresholdDateTimeUtc", thresholdDateTimeUtc) .CommandText = deleteStatement; await connection.OpenAsync(); return(await command.ExecuteNonQueryAsync()); } }