예제 #1
0
        public async Task <ActionResult> Sweep()
        {
            if (!_taskScheduler.VerifyAuthToken(Request.Headers["X-AUTH-TOKEN"]))
            {
                return(new HttpUnauthorizedResult());
            }

            var pendingTasks = await _scheduleTaskService.GetPendingTasksAsync();

            var count          = 0;
            var taskParameters = QueryString.Current.ToDictionary();

            if (pendingTasks.Count > 0)
            {
                Virtualize(taskParameters);
            }

            for (var i = 0; i < pendingTasks.Count; i++)
            {
                var task = pendingTasks[i];

                if (i > 0 /*&& (DateTime.UtcNow - _sweepStart).TotalMinutes > _taskScheduler.SweepIntervalMinutes*/)
                {
                    // Maybe a subsequent Sweep call or another machine in a webfarm executed
                    // successive tasks already.
                    // To be able to determine this, we need to reload the entity from the database.
                    // The TaskExecutor will exit when the task should be in running state then.
                    _services.DbContext.ReloadEntity(task);
                    task.LastHistoryEntry = _scheduleTaskService.GetLastHistoryEntryByTaskId(task.Id);
                }

                if (task.IsPending)
                {
                    await _taskExecutor.ExecuteAsync(task, taskParameters);

                    count++;
                }
            }

            return(Content("{0} of {1} pending tasks executed".FormatInvariant(count, pendingTasks.Count)));
        }
        private async Task Run(int taskId, HttpContext context, ITaskStore taskStore, ITaskExecutor executor, IDictionary <string, string> taskParameters)
        {
            var task = await taskStore.GetTaskByIdAsync(taskId);

            if (task == null)
            {
                context.Response.StatusCode = StatusCodes.Status404NotFound;
                return;
            }

            await Virtualize(context, taskParameters);

            await executor.ExecuteAsync(task, context, taskParameters);

            context.Response.StatusCode = StatusCodes.Status200OK;
            await context.Response.WriteAsync($"Task '{task.Name}' executed.");
        }
        private async Task Poll(HttpContext context, ITaskStore taskStore, ITaskExecutor executor, IDictionary <string, string> taskParameters)
        {
            var pendingTasks = await taskStore.GetPendingTasksAsync();

            var numTasks    = pendingTasks.Count;
            var numExecuted = 0;

            if (pendingTasks.Any())
            {
                await Virtualize(context, taskParameters);
            }

            for (var i = 0; i < numTasks; i++)
            {
                var task = pendingTasks[i];

                if (i > 0 /*&& (DateTime.UtcNow - _sweepStart).TotalMinutes > _taskScheduler.SweepIntervalMinutes*/)
                {
                    // Maybe a subsequent Sweep call or another machine in a webfarm executed
                    // successive tasks already.
                    // To be able to determine this, we need to reload the entity from the database.
                    // The TaskExecutor will exit when the task should be in running state then.
                    await taskStore.ReloadTaskAsync(task);

                    task.LastExecution = await taskStore.GetLastExecutionInfoByTaskIdAsync(task.Id);
                }

                if (task.IsPending)
                {
                    await executor.ExecuteAsync(task, context, taskParameters);

                    numExecuted++;
                }
            }

            context.Response.StatusCode = StatusCodes.Status200OK;
            await context.Response.WriteAsync($"{numExecuted} of {numTasks} pending tasks executed.");
        }
예제 #4
0
		private async Task<TaskExecutionResult> ExecuteTaskAsync ( TaskExecutionContext executionContext )
		{
			ITaskExecutor taskExecutor = null;
			DateTimeOffset retryAt = DateTimeOffset.UtcNow;

			IQueuedTask dequeuedTask = executionContext
				.TaskToken
				.DequeuedTask;

			try
			{
				//Check for cancellation before we start execution
				executionContext.StartTimingExecution();
				executionContext.ThrowIfCancellationRequested();

				//Attempt to resolve and run task executor
				if ( ( taskExecutor = ResolveTaskExecutor( dequeuedTask ) ) != null )
				{
					mLogger.DebugFormat( "Beginning task execution. Task id = {0}.",
						dequeuedTask.Id );

					//Execute task
					await taskExecutor.ExecuteAsync( dequeuedTask.Payload,
						executionContext );

					mLogger.DebugFormat( "Task execution completed. Task id = {0}.",
						dequeuedTask.Id );

					//Ensure we have a result - since no exception was thrown 
					//	and no result explicitly set, assume success.
					if ( !executionContext.HasResult )
						executionContext.NotifyTaskCompleted();
				}
			}
			catch ( OperationCanceledException )
			{
				//User code has observed cancellation request 
				executionContext?.NotifyCancellationObserved();
			}
			catch ( Exception exc )
			{
				mLogger.Error( "Error executing queued task",
					exception: exc );

				bool isRecoverable = mOptions.IsTaskErrorRecoverable( dequeuedTask,
					exc );

				executionContext?.NotifyTaskErrored( new QueuedTaskError( exc ),
					isRecoverable: isRecoverable );
			}
			finally
			{
				executionContext.StopTimingExecution();
			}

			//Compute the amount of time to delay task execution
			//	if execution failed
			if ( executionContext.HasResult
				&& executionContext.ExecutionFailed )
				retryAt = ComputeRetryAt( executionContext.TaskToken );

			return taskExecutor != null
				? new TaskExecutionResult( executionContext.ResultInfo,
					duration: executionContext.Duration,
					retryAt: retryAt,
					faultErrorThresholdCount: mOptions.FaultErrorThresholdCount )
				: null;
		}
예제 #5
0
        public async Task ExecuteAsync(Job job, CancellationToken cancellationToken)
        {
            _logger.LogInformation("Start executing job '{jobId}'.", job.Id);

            // extract patient ids from group if they aren't extracted before for group
            if (job.FilterInfo.FilterScope == FilterScope.Group && !job.Patients.Any())
            {
                _logger.LogInformation("Start extracting patients from group '{groupId}'.", job.FilterInfo.GroupId);

                // For now, the queryParameters is always null.
                // This parameter will be used when we enable filter groups in the future.
                var patientIds = await _groupMemberExtractor.GetGroupPatientsAsync(
                    job.FilterInfo.GroupId,
                    null,
                    job.DataPeriod.End,
                    cancellationToken);

                // set the version id for processed patient
                // the processed patients is empty at the beginning , and will be updated when completing a successful job.
                job.Patients = patientIds.Select(patientId =>
                                                 new PatientWrapper(
                                                     patientId,
                                                     job.FilterInfo.ProcessedPatients.ContainsKey(patientId) ? job.FilterInfo.ProcessedPatients[patientId] : 0));

                _logger.LogInformation(
                    "Extract {patientCount} patients from group '{groupId}', including {newPatientCount} new patients.",
                    patientIds.Count,
                    job.FilterInfo.GroupId,
                    job.Patients.Where(p => p.VersionId == 0).ToList().Count);
            }

            var jobProgressUpdater = _jobProgressUpdaterFactory.Create(job);
            var progressReportTask = Task.Run(() => jobProgressUpdater.Consume(cancellationToken), cancellationToken);

            var tasks = new List <Task <TaskResult> >();

            try
            {
                // add running task to task list for resume job
                tasks.AddRange(job.RunningTasks.Values.Select(taskContext => Task.Run(async() =>
                                                                                      await _taskExecutor.ExecuteAsync(taskContext, jobProgressUpdater, cancellationToken))).ToList());

                var filters = job.FilterInfo.TypeFilters.ToList();
                while (true)
                {
                    if (tasks.Count >= _schedulerConfiguration.MaxConcurrencyCount)
                    {
                        var finishedTask = await Task.WhenAny(tasks);

                        tasks.Remove(finishedTask);

                        if (finishedTask.IsFaulted)
                        {
                            _logger.LogError(finishedTask.Exception, "Process task failed.");

                            // if there is a task failed, we need to wait all the task to be completed
                            // otherwise, the data in other tasks may be missing.
                            await Task.WhenAll(tasks);

                            throw new ExecuteTaskFailedException("Task execution failed", finishedTask.Exception);
                        }
                    }

                    TaskContext taskContext = null;

                    switch (job.FilterInfo.FilterScope)
                    {
                    case FilterScope.System:
                        if (job.NextTaskIndex < job.FilterInfo.TypeFilters.Count())
                        {
                            var typeFilters = new List <TypeFilter>
                            {
                                filters[job.NextTaskIndex]
                            };
                            taskContext = TaskContext.CreateFromJob(job, typeFilters);
                        }

                        break;

                    case FilterScope.Group:
                        if (job.NextTaskIndex * JobConfigurationConstants.NumberOfPatientsPerTask < job.Patients.ToList().Count)
                        {
                            var selectedPatients = job.Patients.Skip(job.NextTaskIndex * JobConfigurationConstants.NumberOfPatientsPerTask)
                                                   .Take(JobConfigurationConstants.NumberOfPatientsPerTask).ToList();
                            taskContext = TaskContext.CreateFromJob(job, filters, selectedPatients);
                        }

                        break;

                    default:
                        // this case should not happen
                        throw new ArgumentOutOfRangeException($"The filterScope {job.FilterInfo.FilterScope} isn't supported now.");
                    }

                    // if there is no new task context created, then all the tasks are generated, break the while loop;
                    if (taskContext == null)
                    {
                        break;
                    }

                    job.RunningTasks[taskContext.Id] = taskContext;
                    tasks.Add(Task.Run(async() => await _taskExecutor.ExecuteAsync(taskContext, jobProgressUpdater, cancellationToken)));
                    _logger.LogInformation("Start processing task '{taskIndex}'", job.NextTaskIndex);
                    job.NextTaskIndex++;
                }

                var taskResults = await Task.WhenAll(tasks);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Process task failed.");
                throw new ExecuteTaskFailedException("Task execution failed", ex);
            }
            finally
            {
                // if there is a task failed, we need to consume all the task contexts before exit jobExecutor
                // otherwise, the task contexts may be consumed and update the job to active after the job is completed in JobManager
                jobProgressUpdater.Complete();
                await progressReportTask;
            }

            _logger.LogInformation("Finish scheduling job '{jobId}'", job.Id);
        }