public bool ChangeState(StateContext context, IState toState, string oldStateName) { try { var filterInfo = GetFilters(context.Job); var electStateContext = new ElectStateContext(context, toState, oldStateName); var electedState = ElectState(electStateContext, filterInfo.ElectStateFilters); var applyStateContext = new ApplyStateContext(context, electedState, oldStateName); ApplyState(applyStateContext, filterInfo.ApplyStateFilters); // State transition was succeeded. return true; } catch (Exception ex) { var failedState = new FailedState(ex) { Reason = "An exception occurred during the transition of job's state" }; var applyStateContext = new ApplyStateContext(context, failedState, oldStateName); // We should not use any state changed filters, because // some of the could cause an exception. ApplyState(applyStateContext, Enumerable.Empty<IApplyStateFilter>()); // State transition was failed due to exception. return false; } }
public IState ApplyState(ApplyStateContext context) { var handlers = _stateHandlersThunk(context.Storage); foreach (var handler in handlers.GetHandlers(context.OldStateName)) { handler.Unapply(context, context.Transaction); } context.Transaction.SetJobState(context.BackgroundJob.Id, context.NewState); foreach (var handler in handlers.GetHandlers(context.NewState.Name)) { handler.Apply(context, context.Transaction); } if (context.NewState.IsFinal) { context.Transaction.ExpireJob(context.BackgroundJob.Id, context.JobExpirationTimeout); } else { context.Transaction.PersistJob(context.BackgroundJob.Id); } return context.NewState; }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.AddToSet( "failed", context.JobId, JobHelper.ToTimestamp(DateTime.UtcNow)); }
public BackgroundJob Create(CreateContext context) { var parameters = context.Parameters.ToDictionary(x => x.Key, x => JobHelper.ToJson(x.Value)); var createdAt = DateTime.UtcNow; var jobId = context.Connection.CreateExpiredJob( context.Job, parameters, createdAt, TimeSpan.FromHours(1)); var backgroundJob = new BackgroundJob(jobId, context.Job, createdAt); if (context.InitialState != null) { using (var transaction = context.Connection.CreateWriteTransaction()) { var applyContext = new ApplyStateContext( context.Storage, context.Connection, transaction, backgroundJob, context.InitialState, null); _stateMachine.ApplyState(applyContext); transaction.Commit(); } } return backgroundJob; }
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) { var awaitingState = context.NewState as AwaitingState; if (awaitingState != null) { context.JobExpirationTimeout = awaitingState.Expiration; } }
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) { var failedState = context.NewState as FailedState; if (failedState != null) { Logger.ErrorException( String.Format("Background job #{0} was failed with an exception.", context.JobId), failedState.Exception); } }
public void Ctor_ShouldSetPropertiesCorrectly() { var context = new ApplyStateContext( _stateContext.Object, _newState.Object, OldState); Assert.Equal(OldState, context.OldStateName); Assert.Same(_newState.Object, context.NewState); Assert.Same(_job, context.Job); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { var enqueuedState = context.NewState as EnqueuedState; if (enqueuedState == null) { throw new InvalidOperationException(String.Format( "`{0}` state handler can be registered only for the Enqueued state.", typeof(Handler).FullName)); } transaction.AddToQueue(enqueuedState.Queue, context.JobId); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { var scheduledState = context.NewState as ScheduledState; if (scheduledState == null) { throw new InvalidOperationException(String.Format( "`{0}` state handler can be registered only for the Scheduled state.", typeof(Handler).FullName)); } var timestamp = JobHelper.ToTimestamp(scheduledState.EnqueueAt); transaction.AddToSet("schedule", context.JobId, timestamp); }
public void ApplyState(IWriteOnlyTransaction transaction, ApplyStateContext context) { var filterInfo = GetFilters(context.Job); var filters = filterInfo.ApplyStateFilters; foreach (var state in context.TraversedStates) { transaction.AddJobState(context.JobId, state); } foreach (var handler in _handlers.GetHandlers(context.OldStateName)) { handler.Unapply(context, transaction); } foreach (var filter in filters) { filter.OnStateUnapplied(context, transaction); } transaction.SetJobState(context.JobId, context.NewState); foreach (var handler in _handlers.GetHandlers(context.NewState.Name)) { handler.Apply(context, transaction); } foreach (var filter in filters) { filter.OnStateApplied(context, transaction); } if (context.NewState.IsFinal) { transaction.ExpireJob(context.JobId, context.JobExpirationTimeout); } else { transaction.PersistJob(context.JobId); } }
public IState ApplyState(ApplyStateContext initialContext) { var filterInfo = GetFilters(initialContext.BackgroundJob.Job); var electFilters = filterInfo.ElectStateFilters; var applyFilters = filterInfo.ApplyStateFilters; // Electing a a state var electContext = new ElectStateContext(initialContext); foreach (var filter in electFilters) { filter.OnStateElection(electContext); } foreach (var state in electContext.TraversedStates) { initialContext.Transaction.AddJobState(electContext.BackgroundJob.Id, state); } // Applying the elected state var context = new ApplyStateContext(initialContext.Transaction, electContext) { JobExpirationTimeout = initialContext.JobExpirationTimeout }; foreach (var filter in applyFilters) { filter.OnStateUnapplied(context, context.Transaction); } foreach (var filter in applyFilters) { filter.OnStateApplied(context, context.Transaction); } return _innerStateMachine.ApplyState(context); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { }
void IApplyStateFilter.OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction) { }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromList("deleted", context.BackgroundJob.Id); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.InsertToList("succeeded", context.JobId); transaction.TrimList("succeeded", 0, 99); }
public void Ctor_ShouldSetPropertiesCorrectly() { var context = new ApplyStateContext( _storage.Object, _connection.Object, _transaction.Object, _backgroundJob.Object, _newState.Object, OldState); Assert.Same(_storage.Object, context.Storage); Assert.Same(_connection.Object, context.Connection); Assert.Same(_transaction.Object, context.Transaction); Assert.Same(_backgroundJob.Object, context.BackgroundJob); Assert.Equal(OldState, context.OldStateName); Assert.Same(_newState.Object, context.NewState); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromSet("failed", context.JobId); }
public IState ChangeState(StateChangeContext context) { // To ensure that job state will be changed only from one of the // specified states, we need to ensure that other users/workers // are not able to change the state of the job during the // execution of this method. To guarantee this behavior, we are // using distributed application locks and rely on fact, that // any state transitions will be made only within a such lock. using (context.Connection.AcquireDistributedJobLock(context.BackgroundJobId, JobLockTimeout)) { var jobData = GetJobData(context); if (jobData == null) { return(null); } if (context.ExpectedStates != null && !context.ExpectedStates.Contains(jobData.State, StringComparer.OrdinalIgnoreCase)) { return(null); } var stateToApply = context.NewState; try { jobData.EnsureLoaded(); } catch (JobLoadException ex) { // This happens when Hangfire couldn't find the target method, // serialized within a background job. There are many reasons // for this case, including refactored code, or a missing // assembly reference due to a mistake or erroneous deployment. // // The problem is that in this case we can't get any filters, // applied at a method or a class level, and we can't proceed // with the state change without breaking a consistent behavior: // in some cases our filters will be applied, and in other ones // will not. // TODO 1.X/2.0: // There's a problem with filters related to handling the states // which ignore this exception, i.e. fitlers for the FailedState // and the DeletedState, such as AutomaticRetryAttrubute filter. // // We should document that such a filters may not be fired, when // we can't find a target method, and these filters should be // applied only at the global level to get consistent results. // // In 2.0 we should have a special state for all the errors, when // Hangfire doesn't know what to do, without any possibility to // add method or class-level filters for such a state to provide // the same behavior no matter what. if (!stateToApply.IgnoreJobLoadException) { stateToApply = new FailedState(ex.InnerException) { Reason = $"Can not change the state to '{stateToApply.Name}': target method was not found." }; } } using (var transaction = context.Connection.CreateWriteTransaction()) { var applyContext = new ApplyStateContext( context.Storage, context.Connection, transaction, new BackgroundJob(context.BackgroundJobId, jobData.Job, jobData.CreatedAt), stateToApply, jobData.State); var appliedState = _stateMachine.ApplyState(applyContext); transaction.Commit(); return(appliedState); } } }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.IncrementCounter("stats:succeeded"); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { var enqueuedState = context.NewState as EnqueuedState; if (enqueuedState == null) { throw new InvalidOperationException( $"`{typeof (Handler).FullName}` state handler can be registered only for the Enqueued state."); } transaction.AddToQueue(enqueuedState.Queue, context.BackgroundJob.Id); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromSet("schedule", context.BackgroundJob.Id); }
private void ApplyState(ApplyStateContext context, IEnumerable<IApplyStateFilter> filters) { using (var transaction = context.Connection.CreateWriteTransaction()) { foreach (var handler in _handlers.GetHandlers(context.OldStateName)) { handler.Unapply(context, transaction); } foreach (var filter in filters) { filter.OnStateUnapplied(context, transaction); } transaction.SetJobState(context.JobId, context.NewState); foreach (var handler in _handlers.GetHandlers(context.NewState.Name)) { handler.Apply(context, transaction); } foreach (var filter in filters) { filter.OnStateApplied(context, transaction); } if (context.NewState.IsFinal) { transaction.ExpireJob(context.JobId, context.JobExpirationTimeout); } else { transaction.PersistJob(context.JobId); } transaction.Commit(); } }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.AddToSet("awaiting", context.JobId, JobHelper.ToTimestamp(DateTime.UtcNow)); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.InsertToList("succeeded", context.JobId); transaction.TrimList("succeeded", 0, RedisStorage.SucceededListSize); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromSet("awaiting", context.JobId); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.DecrementCounter("stats:deleted"); }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.AddToSet("processing", context.BackgroundJob.Id, JobHelper.ToTimestamp(DateTime.UtcNow)); }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromList("succeeded", context.JobId); }
/// <inheritdoc /> public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction) { if (context.NewState is ScheduledState && context.NewState.Reason != null && context.NewState.Reason.StartsWith("Retry attempt")) { transaction.AddToSet("retries", context.BackgroundJob.Id); } }
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.InsertToList("deleted", context.BackgroundJob.Id); transaction.TrimList("deleted", 0, 99); }
/// <inheritdoc /> public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction) { if (context.OldStateName == ScheduledState.StateName) { transaction.RemoveFromSet("retries", context.BackgroundJob.Id); } }
private IState ChangeState( StateChangeContext context, BackgroundJob backgroundJob, IState toState, string oldStateName) { using (var transaction = context.Connection.CreateWriteTransaction()) { var applyContext = new ApplyStateContext( context.Storage, context.Connection, transaction, backgroundJob, toState, oldStateName); var appliedState = _stateMachine.ApplyState(applyContext); transaction.Commit(); return appliedState; } }
public void OnStateUnapplied(ApplyStateContext content, IWriteOnlyTransaction transcation) { }
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction) { transaction.RemoveFromSet("processing", context.BackgroundJob.Id); }
public IState ChangeState(StateChangeContext context) { // To ensure that job state will be changed only from one of the // specified states, we need to ensure that other users/workers // are not able to change the state of the job during the // execution of this method. To guarantee this behavior, we are // using distributed application locks and rely on fact, that // any state transitions will be made only within a such lock. using (context.Connection.AcquireDistributedJobLock(context.BackgroundJobId, JobLockTimeout)) { var jobData = GetJobData(context); if (jobData == null) { return(null); } if (context.ExpectedStates != null && !context.ExpectedStates.Contains(jobData.State, StringComparer.OrdinalIgnoreCase)) { return(null); } var stateToApply = context.NewState; try { jobData.EnsureLoaded(); } catch (JobLoadException ex) { // This happens when Hangfire couldn't find the target method, // serialized within a background job. There are many reasons // for this case, including refactored code, or a missing // assembly reference due to a mistake or erroneous deployment. // // The problem is that in this case we can't get any filters, // applied at a method or a class level, and we can't proceed // with the state change without breaking a consistent behavior: // in some cases our filters will be applied, and in other ones // will not. if (!stateToApply.IgnoreJobLoadException) { stateToApply = new FailedState(ex.InnerException) { Reason = $"Can not change the state to '{stateToApply.Name}': target method was not found." }; } } using (var transaction = context.Connection.CreateWriteTransaction()) { var applyContext = new ApplyStateContext( context.Storage, context.Connection, transaction, new BackgroundJob(context.BackgroundJobId, jobData.Job, jobData.CreatedAt), stateToApply, jobData.State, context.Profiler); // State changing process can fail due to an exception in state filters themselves, // and DisableFilters property will cause state machine to perform a state transition // without calling any filters. This is required when all the other state change // attempts failed and we need to remove such a job from the processing pipeline. // In this case all the filters are ignored, which may lead to confusion, so it's // highly recommended to use the DisableFilters property only when changing state // to the FailedState. var stateMachine = context.DisableFilters ? _innerStateMachine : _stateMachine; var appliedState = stateMachine.ApplyState(applyContext); transaction.Commit(); return(appliedState); } } }