public LatencyTimeoutAttributeFacts() { var state = new ProcessingState("Default", "1"); _context = new ElectStateContextMock(); _context.ApplyContext.BackgroundJob.Id = JobId; _context.ApplyContext.NewStateObject = state; }
/// <inheritdoc /> public void Execute(BackgroundProcessContext context) { if (context == null) throw new ArgumentNullException("context"); using (var connection = context.Storage.GetConnection()) using (var fetchedJob = connection.FetchNextJob(_queues, context.CancellationToken)) { context.CancellationToken.ThrowIfCancellationRequested(); try { using (var timeoutCts = new CancellationTokenSource(JobInitializationWaitTimeout)) using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( context.CancellationToken, timeoutCts.Token)) { var processingState = new ProcessingState(context.ServerId, _workerId); var appliedState = _stateChanger.ChangeState(new StateChangeContext( context.Storage, connection, fetchedJob.JobId, processingState, new[] { EnqueuedState.StateName, ProcessingState.StateName }, linkedCts.Token)); // Cancel job processing if the job could not be loaded, was not in the initial state expected // or if a job filter changed the state to something other than processing state if (appliedState == null || !appliedState.Name.Equals(ProcessingState.StateName, StringComparison.OrdinalIgnoreCase)) { // We should re-queue a job identifier only when graceful shutdown // initiated. context.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 state = PerformJob(context, connection, fetchedJob.JobId); if (state != null) { // Ignore return value, because we should not do anything when current state is not Processing. _stateChanger.ChangeState(new StateChangeContext( context.Storage, connection, fetchedJob.JobId, state, 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; } } }
private void ProcessJob( string jobId, IStorageConnection connection, IJobPerformanceProcess process, CancellationToken shutdownToken) { var stateMachine = _context.StateMachineFactory.Create(connection); var processingState = new ProcessingState(_context.ServerId, _context.WorkerNumber); if (!stateMachine.TryToChangeState( jobId, processingState, new[] { EnqueuedState.StateName, ProcessingState.StateName })) { 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. IState state; try { var jobData = connection.GetJobData(jobId); jobData.EnsureLoaded(); var cancellationToken = new ServerJobCancellationToken( jobId, connection, _context, shutdownToken); var performContext = new PerformContext( _context, connection, jobId, jobData.Job, jobData.CreatedAt, cancellationToken); var latency = (DateTime.UtcNow - jobData.CreatedAt).TotalMilliseconds; var duration = Stopwatch.StartNew(); process.Run(performContext, jobData.Job); duration.Stop(); state = new SucceededState((long) latency, duration.ElapsedMilliseconds); } catch (OperationCanceledException) { throw; } catch (JobPerformanceException ex) { state = new FailedState(ex.InnerException) { Reason = ex.Message }; } catch (Exception ex) { state = new FailedState(ex) { Reason = "Internal Hangfire Server exception occurred. Please, report it to Hangfire developers." }; } // Ignore return value, because we should not do // anything when current state is not Processing. stateMachine.TryToChangeState(jobId, state, new[] { ProcessingState.StateName }); }
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 jobCancellationToken = new ServerJobCancellationToken( fetchedJob.JobId, connection, _context, cancellationToken); var state = PerformJob(fetchedJob.JobId, connection, jobCancellationToken); 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; } } }