Esempio n. 1
0
        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.FromDays(30));

            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,
                        oldStateName: null,
                        profiler: context.Profiler);

                    _stateMachine.ApplyState(applyContext);

                    transaction.Commit();
                }
            }

            return(backgroundJob);
        }
        public static void EnqueueBackgroundJob(
            [NotNull] this IStateMachine stateMachine,
            [NotNull] JobStorage storage,
            [NotNull] IStorageConnection connection,
            [NotNull] IWriteOnlyTransaction transaction,
            [NotNull] RecurringJobEntity recurringJob,
            [NotNull] BackgroundJob backgroundJob,
            [CanBeNull] string reason,
            [NotNull] IProfiler profiler)
        {
            if (stateMachine == null)
            {
                throw new ArgumentNullException(nameof(stateMachine));
            }
            if (storage == null)
            {
                throw new ArgumentNullException(nameof(storage));
            }
            if (connection == null)
            {
                throw new ArgumentNullException(nameof(connection));
            }
            if (transaction == null)
            {
                throw new ArgumentNullException(nameof(transaction));
            }
            if (recurringJob == null)
            {
                throw new ArgumentNullException(nameof(recurringJob));
            }
            if (backgroundJob == null)
            {
                throw new ArgumentNullException(nameof(backgroundJob));
            }
            if (profiler == null)
            {
                throw new ArgumentNullException(nameof(profiler));
            }

            var state = new EnqueuedState {
                Reason = reason
            };

            if (recurringJob.Queue != null)
            {
                state.Queue = recurringJob.Queue;
            }

            stateMachine.ApplyState(new ApplyStateContext(
                                        storage,
                                        connection,
                                        transaction,
                                        backgroundJob,
                                        state,
                                        null,
                                        profiler));
        }
Esempio n. 3
0
        private IState ChangeState(
            StateChangeContext context, BackgroundJob backgroundJob, IState toState, string oldStateName)
        {
            Exception exception = null;

            for (var i = 0; i < MaxStateChangeAttempts; i++)
            {
                try
                {
                    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);
                    }
                }
                catch (Exception ex)
                {
                    exception = ex;
                }
            }

            var failedState = new FailedState(exception)
            {
                Reason = $"Failed to change state to a '{toState.Name}' one due to an exception after {MaxStateChangeAttempts} retry attempts"
            };

            using (var transaction = context.Connection.CreateWriteTransaction())
            {
                _coreStateMachine.ApplyState(new ApplyStateContext(
                                                 context.Storage,
                                                 context.Connection,
                                                 transaction,
                                                 backgroundJob,
                                                 failedState,
                                                 oldStateName));

                transaction.Commit();
            }

            return(failedState);
        }
Esempio n. 4
0
        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)
            {
                electContext.Profiler.InvokeMeasured(
                    Tuple.Create(filter, electContext),
                    InvokeOnStateElection,
                    $"OnStateElection for {electContext.BackgroundJob.Id}");
            }

            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)
            {
                context.Profiler.InvokeMeasured(
                    Tuple.Create(filter, context),
                    InvokeOnStateUnapplied,
                    $"OnStateUnapplied for {context.BackgroundJob.Id}");
            }

            foreach (var filter in applyFilters)
            {
                context.Profiler.InvokeMeasured(
                    Tuple.Create(filter, context),
                    InvokeOnStateApplied,
                    $"OnStateApplied for {context.BackgroundJob.Id}");
            }

            return(_innerStateMachine.ApplyState(context));
        }
Esempio n. 5
0
        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);
            }
        }
Esempio n. 6
0
        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));
        }
Esempio n. 7
0
        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);
                }
            }
        }