/// <summary> /// Starts the main loop. /// </summary> /// <param name='primary'> /// (Mandatory) Primary Job Consumer, this is the one that will have priority on Job consuption /// (e.g. local machine consumer). /// </param> /// <param name='secundary'> /// (Optional) Lower priority consumer. /// </param> /// <remarks> /// THIS BLOCKS EXECUTION! Execute in a thread if you don't want to block the main thread! /// Tweak the consumer score to achieve the best performance. /// </remarks> public void Start(IJobConsumer primary, IJobConsumer secundary) { Debug.Assert(primary != null); if (primary == null) throw new ArgumentNullException("You have to set the primary consumer!"); while (true) { Job nextJob; lock (mQueueLocker) { while (mJobQueue.Count == 0) Monitor.Wait(mQueueLocker); nextJob = mJobQueue.Dequeue(); } // We can't get here without Dequeueing at least once! if (nextJob == null) break; IJobConsumer nextConsumer = null; while (nextConsumer == null) { if (primary.HasAvailableWorkers) { nextConsumer = primary; } else if (secundary != null && secundary.HasAvailableWorkers) { nextConsumer = secundary; } } nextConsumer.Consume(nextJob); } }
private static void SetConsumerDelegates(IJobConsumer consumer, JobManager manager, string name) { consumer.JobStarted += (cons, job) => { Console.WriteLine("JOB {0} - Started job {1}", name, job.Id); }; consumer.JobFinished += (cons, job) => { Console.WriteLine("JOB {0} - Finished job {1}", name, job.Id); }; // TODO: Add reason to the delegate! consumer.JobFailed += (cons, job) => { Console.WriteLine("JOB {0} - Failed to consume job {1}", name, job.Id); // Add it back to the queue manager.EnqueueJob(job); }; }
async Task RunJob(PipeContext context, IJobConsumer <TJob> jobConsumer) { var jobContext = context.GetPayload <JobContext <TJob> >(); RetryPolicyContext <JobContext <TJob> > policyContext = _retryPolicy.CreatePolicyContext(jobContext); try { await jobContext.NotifyStarted().ConfigureAwait(false); await jobConsumer.Run(jobContext).ConfigureAwait(false); await jobContext.NotifyCompleted().ConfigureAwait(false); } catch (OperationCanceledException exception) { if (jobContext.CancellationToken == exception.CancellationToken) { await jobContext.NotifyCanceled("Operation canceled").ConfigureAwait(false); } } catch (Exception exception) { if (!policyContext.CanRetry(exception, out RetryContext <JobContext <TJob> > retryContext)) { if (_retryPolicy.IsHandled(exception)) { context.GetOrAddPayload(() => retryContext); await retryContext.RetryFaulted(exception).ConfigureAwait(false); } await jobContext.NotifyFaulted(exception).ConfigureAwait(false); return; } var currentRetryAttempt = jobContext.RetryAttempt; for (var retryIndex = 0; retryIndex < currentRetryAttempt; retryIndex++) { if (!retryContext.CanRetry(exception, out retryContext)) { if (_retryPolicy.IsHandled(exception)) { context.GetOrAddPayload(() => retryContext); await retryContext.RetryFaulted(exception).ConfigureAwait(false); } await jobContext.NotifyFaulted(exception).ConfigureAwait(false); return; } } var delay = retryContext.Delay ?? TimeSpan.Zero; await jobContext.NotifyFaulted(exception, delay).ConfigureAwait(false); } finally { policyContext.Dispose(); } }