Example #1
0
 /// <summary>
 /// Wraps a call to <see cref="TaskQueueDirective.Parse{T}(Common.ApplicationTaskQueue.ApplicationTask)"/>
 /// to extract any directive supplied to the <paramref name="job" />.
 /// </summary>
 /// <param name="job">The job to retrieve the directive for.</param>
 /// <returns>The directive, or null if no directive passed.</returns>
 public static TaskQueueDirective GetTaskQueueDirective(this TaskProcessorJob job)
 {
     return(job.GetTaskQueueDirective <TaskQueueDirective>());
 }
        /// <summary>
        /// Handles when a job from a task requires processing.
        /// Note that all tasks/jobs in the queue use the same task type ID,
        /// so the directive <see cref="BackgroundOperationTaskQueueDirective.BackgroundOperationName"/>
        /// is used to identify which tasks should be executed by
        /// which background operation.
        /// </summary>
        /// <param name="job">The job to process.</param>
        protected virtual void ProcessJobHandler(TaskProcessorJob job)
        {
            // Sanity.
            if (null == job)
            {
                return;
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // What is the current state?
            var state = job.AppTaskState;

            // Update the progress of the task in the task queue.
            try
            {
                this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);
            }
            catch
            {
                // Could not mark the task as assigned to a processor.
                SysUtils.ReportToEventLog
                (
                    $"Could not mark task {job.AppTaskId} as assigned to a processor (queue id: {job.AppTaskQueueId}, state: {state}).",
                    System.Diagnostics.EventLogEntryType.Warning
                );
                return;
            }

            // Sanity.
            if (null == job.Data?.Value)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no application task (queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                );
                return;
            }

            // Deserialize the background directive.
            var backgroundOperationDirective = job.GetTaskQueueDirective <BackgroundOperationTaskQueueDirective>();

            if (null == backgroundOperationDirective)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no background operation name loaded (queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                );
                return;
            }

            // If we have a directive then extract it.
            var dir = backgroundOperationDirective.GetParsedInternalDirective();

            // If it is a broadcast directive, then was it generated on the same server?
            // If so then ignore.
            if (dir is BroadcastDirective broadcastDirective)
            {
                if (broadcastDirective.GeneratedFromGuid == TaskQueueBackgroundOperationManager.CurrentServer.ServerID)
                {
                    return;
                }
            }

            // Find the background operation to run.
            TaskQueueBackgroundOperationOverview bo = null;

            lock (_lock)
            {
                if (false == this.BackgroundOperations.TryGetValue(backgroundOperationDirective.BackgroundOperationName, out bo))
                {
                    // We have no registered background operation to handle the callback.
                    SysUtils.ReportErrorToEventLog
                    (
                        $"No background operation found with name {backgroundOperationDirective.BackgroundOperationName}(queue: {job.AppTaskQueueId}, task id: {job.AppTaskId})."
                    );
                    return;
                }
            }

            // Should we repeat?
            DateTime?nextRun = null;

            switch (bo.BackgroundOperation.RepeatType)
            {
            case TaskQueueBackgroundOperationRepeatType.Interval:

                // Add the interval to the current datetime.
                if (bo.BackgroundOperation.Interval.HasValue)
                {
                    nextRun = DateTime.UtcNow.Add(bo.BackgroundOperation.Interval.Value);
                }
                break;

            case TaskQueueBackgroundOperationRepeatType.Schedule:

                // Get the next execution time from the schedule.
                nextRun = bo.BackgroundOperation.Schedule?.GetNextExecution(DateTime.Now);
                break;
            }

            // If we have a next run time then re-run.
            if (null != nextRun)
            {
                // Bind to the completed event ( called always ) of the job.
                // That way even if the job is canceled, fails, or finishes successfully
                // ...we always schedule the next run.
                job.ProcessingCompleted += (s, op)
                                           =>
                {
                    // Ensure that if two threads both run this at once we don't end up with a race condition.
                    lock (_lock)
                    {
                        // Cancel any future executions (we only want the single one created below).
                        this.CancelFutureExecutions(bo.BackgroundOperation.Name);

                        // Now schedule it to run according to the interval.
                        this.RunOnce
                        (
                            bo.BackgroundOperation.Name,
                            nextRun.Value.ToUniversalTime(),
                            dir
                        );
                    }
                };
            }

            // Perform the action.
            try
            {
                // Mark the background operation as running.
                bo.Status  = TaskQueueBackgroundOperationStatus.Running;
                bo.LastRun = DateTime.UtcNow;

                // Delegate to the background operation.
                bo.BackgroundOperation.RunJob(job, dir);
            }
            catch (Exception e)
            {
                // Exception.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateFailed,
                    e.Message,
                    false
                );
                // TODO: throw?
            }
            finally
            {
                // If the status is running then stop it.
                if (bo.Status == TaskQueueBackgroundOperationStatus.Running)
                {
                    bo.Status = TaskQueueBackgroundOperationStatus.Stopped;
                }
            }
        }
Example #3
0
        /// <summary>
        /// Handles when a job from a task requires processing.
        /// Note that all tasks/jobs in the queue use the same task type ID,
        /// so the directive <see cref="BackgroundOperationTaskQueueDirective.BackgroundOperationName"/>
        /// is used to identify which tasks should be executed by
        /// which background operation.
        /// </summary>
        /// <param name="job">The job to process.</param>
        protected virtual void ProcessJobHandler(TaskProcessorJob job)
        {
            // Sanity.
            if (null == job)
            {
                return;
            }

            // Ensure cancellation has not been requested.
            job.ThrowIfCancellationRequested();

            // Update the progress of the task in the task queue.
            this.TaskProcessor.UpdateTaskAsAssignedToProcessor(job);

            // Sanity.
            if (null == job.Data?.Value)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no application task (queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                );
                return;
            }

            // Deserialize the background directive.
            var backgroundOperationDirective = job.GetTaskQueueDirective <BackgroundOperationTaskQueueDirective>();

            if (null == backgroundOperationDirective)
            {
                // This is an issue.  We have no way to decide what background operation should run it.  Die.
                SysUtils.ReportErrorToEventLog
                (
                    $"Job loaded with no background operation name loaded (queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                );
                return;
            }

            // If we have a directive then extract it.
            var dir = backgroundOperationDirective.GetParsedInternalDirective();

            // If it is a broadcast directive, then was it generated on the same server?
            // If so then ignore.
            if (dir is BroadcastDirective broadcastDirective)
            {
                if (broadcastDirective.GeneratedFromGuid == TaskQueueBackgroundOperationManager.CurrentServer.ServerID)
                {
                    return;
                }
            }

            // Find the background operation to run.
            TaskQueueBackgroundOperation bo = null;

            lock (_lock)
            {
                if (false == this.BackgroundOperations.TryGetValue(backgroundOperationDirective.BackgroundOperationName, out bo))
                {
                    // We have no registered background operation to handle the callback.
                    SysUtils.ReportErrorToEventLog
                    (
                        $"No background operation found with name {backgroundOperationDirective.BackgroundOperationName}(queue: {job.AppTaskQueueId}, task type: {job.AppTaskId})."
                    );
                    return;
                }
            }

            // If it is recurring then use the ProcessingComplete event to schedule the next execution.
            if (bo.Recurring && bo.Interval.HasValue)
            {
                // Bind to the completed event ( called always ) of the job.
                // That way even if the job is canceled, fails, or finishes successfully
                // ...we always schedule the next run.
                job.ProcessingCompleted += (s, op)
                                           => this.RunOnce
                                           (
                    bo.Name,
                    DateTime.UtcNow.Add(bo.Interval.Value),
                    dir
                                           );
            }

            // Perform the action.
            try
            {
                // Delegate to the background operation.
                bo.RunJob(job, dir);
            }
            catch (Exception e)
            {
                // Exception.
                this.TaskProcessor.UpdateTaskInfo
                (
                    job.Data?.Value,
                    MFTaskState.MFTaskStateFailed,
                    e.Message,
                    false
                );
                // TODO: throw?
            }
        }