private async Task ResumeIdleWorkflows(CancellationToken cancellationToken) { var instances = await _workflowInstanceStore.FindManyAsync(new WorkflowStatusSpecification(WorkflowStatus.Idle), cancellationToken : cancellationToken).ToList(); if (instances.Any()) { _logger.LogInformation("Found {WorkflowInstanceCount} workflows with status 'Idle'. Resuming each one of them", instances.Count); } else { _logger.LogInformation("Found no workflows with status 'Id'. Nothing to resume"); } foreach (var instance in instances) { await using var correlationLockHandle = await _distributedLockProvider.AcquireLockAsync(instance.CorrelationId, _elsaOptions.DistributedLockTimeout, cancellationToken); if (correlationLockHandle == null) { _logger.LogWarning("Failed to acquire lock on correlation {CorrelationId} for workflow instance {WorkflowInstanceId}", instance.CorrelationId, instance.Id); continue; } _logger.LogInformation("Resuming {WorkflowInstanceId}", instance.Id); var input = await GetWorkflowInputAsync(instance, cancellationToken); await _workflowInstanceDispatcher.DispatchAsync(new ExecuteWorkflowInstanceRequest(instance.Id, Input : input), cancellationToken); } }
private async IAsyncEnumerable <WorkflowInstance> InterruptActivityTypeInternalAsync(string activityType, object?input, [EnumeratorCancellation] CancellationToken cancellationToken) { var suspendedWorkflows = await _workflowInstanceStore.FindManyAsync(new BlockingActivityTypeSpecification(activityType), cancellationToken : cancellationToken).ToList(); var workflowInstanceIds = suspendedWorkflows.Select(x => x.Id).Distinct().ToList(); var workflowInstances = await _workflowInstanceStore.FindManyAsync(new ManyWorkflowInstanceIdsSpecification(workflowInstanceIds), cancellationToken : cancellationToken).ToDictionary(x => x.Id); foreach (var suspendedWorkflow in suspendedWorkflows) { var workflowInstance = workflowInstances[suspendedWorkflow.Id]; yield return(await InterruptActivityTypeAsync(workflowInstance, activityType, input, cancellationToken)); } }
public async Task ExecuteAsync(CancellationToken cancellationToken = default) { var threshold = _clock.GetCurrentInstant().Minus(_options.TimeToLive); var specification = new WorkflowCreatedBeforeSpecification(threshold); var currentPage = 0; var take = _options.BatchSize; var orderBy = new OrderBy <WorkflowInstance>(x => x.CreatedAt, SortDirection.Descending); var collectedWorkflowInstanceIds = new List <string>(); // Collect workflow instances to be deleted. while (true) { var paging = Paging.Page(currentPage++, take); var workflowInstances = await _workflowInstanceStore .FindManyAsync(specification, orderBy, paging, cancellationToken) .ToList(); var filteredWorkflowInstances = await _retentionFilterPipeline.FilterAsync(workflowInstances, cancellationToken).ToList(); collectedWorkflowInstanceIds.AddRange(filteredWorkflowInstances.Select(x => x.Id)); if (workflowInstances.Count < take) { break; } } // Delete collected workflow instances. await DeleteManyAsync(collectedWorkflowInstanceIds, cancellationToken); }
private async IAsyncEnumerable <IWorkflowBlueprint> ListActiveInternalAsync([EnumeratorCancellation] CancellationToken cancellationToken) { var workflows = await ListInternalAsync(cancellationToken).ToListAsync(cancellationToken); var publishedWorkflows = workflows.Where(x => x.IsPublished); foreach (var publishedWorkflow in publishedWorkflows) { yield return(publishedWorkflow); } // We also need to consider unpublished workflows for inclusion in case they still have associated active workflow instances. var unpublishedWorkflows = workflows.Where(x => !x.IsPublished).ToDictionary(x => x.VersionId); var unpublishedWorkflowIds = unpublishedWorkflows.Keys; if (!unpublishedWorkflowIds.Any()) { yield break; } var activeWorkflowInstances = await _workflowInstanceStore.FindManyAsync(new UnfinishedWorkflowSpecification().WithWorkflowDefinitionVersionIds(unpublishedWorkflowIds), cancellationToken : cancellationToken).ToList(); var activeUnpublishedWorkflowVersionIds = activeWorkflowInstances.Select(x => x.DefinitionVersionId).Distinct().ToList(); var activeUnpublishedWorkflowVersions = unpublishedWorkflows.Where(x => activeUnpublishedWorkflowVersionIds.Contains(x.Key)).Select(x => x.Value); foreach (var unpublishedWorkflow in activeUnpublishedWorkflowVersions) { yield return(unpublishedWorkflow); } }
public async Task <IActionResult> Handle( [FromQuery(Name = "workflow")] string?workflowDefinitionId = default, [FromQuery(Name = "status")] WorkflowStatus?workflowStatus = default, [FromQuery] string?correlationId = default, [FromQuery] OrderBy?orderBy = default, [FromQuery] string?searchTerm = default, int page = 0, int pageSize = 25, CancellationToken cancellationToken = default) { _stopwatch.Restart(); var specification = Specification <WorkflowInstance> .Identity; if (!string.IsNullOrWhiteSpace(workflowDefinitionId)) { specification = specification.WithWorkflowDefinition(workflowDefinitionId); } if (!string.IsNullOrWhiteSpace(correlationId)) { specification = specification.WithCorrelationId(correlationId); } if (workflowStatus != null) { specification = specification.WithStatus(workflowStatus.Value); } if (!string.IsNullOrWhiteSpace(searchTerm)) { specification = specification.WithSearchTerm(searchTerm); } var orderBySpecification = default(OrderBy <WorkflowInstance>); if (orderBy != null) { orderBySpecification = orderBy switch { OrderBy.Started => OrderBySpecification.OrderByDescending <WorkflowInstance>(x => x.CreatedAt), OrderBy.LastExecuted => OrderBySpecification.OrderByDescending <WorkflowInstance>(x => x.LastExecutedAt !), OrderBy.Finished => OrderBySpecification.OrderByDescending <WorkflowInstance>(x => x.FinishedAt !), _ => OrderBySpecification.OrderByDescending <WorkflowInstance>(x => x.FinishedAt !) }; } var totalCount = await _workflowInstanceStore.CountAsync(specification, cancellationToken : cancellationToken); var paging = Paging.Page(page, pageSize); var workflowInstances = await _workflowInstanceStore.FindManyAsync(specification, orderBySpecification, paging, cancellationToken).ToList(); var items = _mapper.Map <ICollection <WorkflowInstanceSummaryModel> >(workflowInstances); _stopwatch.Stop(); _logger.LogDebug("Handle took {TimeElapsed}", _stopwatch.Elapsed); var model = new PagedList <WorkflowInstanceSummaryModel>(items, page, pageSize, totalCount); return(Json(model, _contentSerializer.GetSettings())); } }
public async Task ExecuteAsync(CancellationToken cancellationToken = default) { await using var handle = await _distributedLockProvider.AcquireLockAsync(GetType ().Name, _elsaOptions.DistributedLockTimeout, cancellationToken); if (handle == null) { return; } var instances = await _workflowInstanceStore.FindManyAsync(new WorkflowStatusSpecification(WorkflowStatus.Running), cancellationToken : cancellationToken).ToList(); if (instances.Any()) { _logger.LogInformation("Found {WorkflowInstanceCount} workflows with status 'Running'. Resuming each one of them", instances.Count); } else { _logger.LogInformation("Found no workflows with status 'Running'. Nothing to resume"); } foreach (var instance in instances) { await using var correlationLockHandle = await _distributedLockProvider.AcquireLockAsync(instance.CorrelationId, _elsaOptions.DistributedLockTimeout, cancellationToken); if (handle == null) { _logger.LogWarning("Failed to acquire lock on correlation {CorrelationId} for workflow instance {WorkflowInstanceId}", instance.CorrelationId, instance.Id); continue; } _logger.LogInformation("Resuming {WorkflowInstanceId}", instance.Id); var scheduledActivities = instance.ScheduledActivities; if (instance.CurrentActivity == null && !scheduledActivities.Any()) { if (instance.BlockingActivities.Any()) { _logger.LogWarning( "Workflow '{WorkflowInstanceId}' was in the Running state, but has no scheduled activities not has a currently executing one. However, it does have blocking activities, so switching to Suspended status", instance.Id); instance.WorkflowStatus = WorkflowStatus.Suspended; await _workflowInstanceStore.SaveAsync(instance, cancellationToken); continue; } _logger.LogWarning("Workflow '{WorkflowInstanceId}' was in the Running state, but has no scheduled activities nor has a currently executing one", instance.Id); continue; } var scheduledActivity = instance.CurrentActivity ?? instance.ScheduledActivities.Peek(); await _workflowInstanceDispatcher.DispatchAsync(new ExecuteWorkflowInstanceRequest(instance.Id, scheduledActivity.ActivityId), cancellationToken); } }
public async Task <IActionResult> Handle(BulkRetryWorkflowsRequest request, CancellationToken cancellationToken = default) { var workflowInstances = (await _store.FindManyAsync(new WorkflowInstanceIdsSpecification(request.WorkflowInstanceIds), cancellationToken: cancellationToken)).ToList(); var faultedWorkflowInstances = workflowInstances.Where(x => x.WorkflowStatus == WorkflowStatus.Faulted).ToList(); foreach (var workflowInstance in faultedWorkflowInstances) { await _reviver.ReviveAndQueueAsync(workflowInstance, cancellationToken); } return(Json(new { ScheduledWorkflowCount = faultedWorkflowInstances.Count }, _serializerSettingsProvider.GetSettings())); }
private async IAsyncEnumerable <RunWorkflowResult> InterruptActivityTypeInternalAsync(string activityType, WorkflowInput?input, [EnumeratorCancellation] CancellationToken cancellationToken) { var bookmarks = await _bookmarkFinder.FindBookmarksAsync(activityType, Enumerable.Empty <IBookmark>(), cancellationToken : cancellationToken); var workflowInstanceIds = bookmarks.Select(x => x.WorkflowInstanceId).Distinct().ToList(); var workflowInstances = await _workflowInstanceStore.FindManyAsync(new ManyWorkflowInstanceIdsSpecification(workflowInstanceIds), cancellationToken : cancellationToken); foreach (var workflowInstance in workflowInstances) { var results = await InterruptActivityTypeAsync(workflowInstance, activityType, input, cancellationToken); foreach (var result in results) { yield return(result); } } }
public async Task ExecuteAsync(CancellationToken cancellationToken = default) { var threshold = _clock.GetCurrentInstant().Minus(_options.TimeToLive); var specification = new WorkflowCreatedBeforeSpecification(threshold).And(new WorkflowFinishedStatusSpecification()); var take = _options.PageSize; IList <string> workflowInstanceIds; do { workflowInstanceIds = (await _workflowInstanceStore.FindManyAsync(specification, new OrderBy <WorkflowInstance>(x => x.CreatedAt, SortDirection.Descending), new Paging(0, take), cancellationToken: cancellationToken)) .Select(x => x.Id).ToList(); _logger.LogInformation("Deleting {WorkflowInstanceCount} workflow instances", workflowInstanceIds.Count); if (workflowInstanceIds.Any()) { await DeleteManyAsync(workflowInstanceIds, cancellationToken); } } while (workflowInstanceIds.Any()); }
public Task <IEnumerable <WorkflowInstance> > FindManyAsync(ISpecification <WorkflowInstance> specification, IOrderBy <WorkflowInstance>?orderBy = default, IPaging?paging = default, CancellationToken cancellationToken = default) => _store.FindManyAsync(specification, orderBy, paging, cancellationToken);