public void Execute(CancellationToken cancellationToken) { using (var connection = _storage.GetConnection()) using (var fetchedJob = connection.FetchNextJob(_context.Queues, cancellationToken)) { try { var stateMachine = _stateMachineFactory.Create(connection); using (var timeoutCts = new CancellationTokenSource(JobInitializationWaitTimeout)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( cancellationToken, timeoutCts.Token)) { var processingState = new ProcessingState(_context.ServerId, _context.WorkerNumber); if (!stateMachine.ChangeState( fetchedJob.JobId, processingState, new[] { EnqueuedState.StateName, ProcessingState.StateName }, linkedCts.Token)) { // We should re-queue a job identifier only when graceful shutdown // initiated. cancellationToken.ThrowIfCancellationRequested(); // We should forget a job in a wrong state, or when timeout exceeded. fetchedJob.RemoveFromQueue(); return; } } // Checkpoint #3. Job is in the Processing state. However, there are // no guarantees that it was performed. We need to re-queue it even // it was performed to guarantee that it was performed AT LEAST once. // It will be re-queued after the JobTimeout was expired. var context = new ServerJobExecutionContext( fetchedJob.JobId, connection, _context, cancellationToken); var state = PerformJob(fetchedJob.JobId, connection, context); if (state != null) { // Ignore return value, because we should not do anything when current state is not Processing. stateMachine.ChangeState(fetchedJob.JobId, state, new[] { ProcessingState.StateName }); } // Checkpoint #4. The job was performed, and it is in the one // of the explicit states (Succeeded, Scheduled and so on). // It should not be re-queued, but we still need to remove its // processing information. fetchedJob.RemoveFromQueue(); // Success point. No things must be done after previous command // was succeeded. } catch (JobAbortedException) { fetchedJob.RemoveFromQueue(); } catch (Exception ex) { Logger.DebugException("An exception occurred while processing a job. It will be re-queued.", ex); fetchedJob.Requeue(); throw; } } }