public static void GetCreatedAt_ReturnsNull_AwaitingState()
    {
        var state = new AwaitingState("1");

        var result = state.GetCreatedAt();

        Assert.Null(result);
    }
Example #2
0
        public string ContinueJobWith(string parentId, [NotNull, InstantHandle] Expression <Func <Task> > action,
                                      JobContinuationOptions continuationOptions = JobContinuationOptions.OnlyOnSucceededState,
                                      JobContinuationOptions atomProgress        = JobContinuationOptions.OnlyOnSucceededState)
        {
            var state = new AwaitingState(parentId, new EnqueuedState(), continuationOptions);

            return(CreateSubatomInternal(action, state, atomProgress));
        }
        private void AddContinuation(ElectStateContext context, AwaitingState awaitingState)
        {
            var connection = context.Connection;
            var parentId   = awaitingState.ParentId;

            // We store continuations as a json array in a job parameter. Since there
            // is no way to add a continuation in an atomic way, we are placing a
            // distributed lock on parent job to prevent race conditions, when
            // multiple threads add continuation to the same parent job.
            using (connection.AcquireDistributedJobLock(parentId, AddJobLockTimeout))
            {
                var continuations = GetContinuations(connection, parentId);

                // Continuation may be already added. This may happen, when outer transaction
                // was failed after adding a continuation last time, since the addition is
                // performed outside of an outer transaction.
                if (!continuations.Exists(x => x.JobId == context.BackgroundJob.Id))
                {
                    continuations.Add(new Continuation {
                        JobId = context.BackgroundJob.Id, Options = awaitingState.Options
                    });

                    // Set continuation only after ensuring that parent job still
                    // exists. Otherwise we could create add non-expiring (garbage)
                    // parameter for the parent job.
                    SetContinuations(connection, parentId, continuations);
                }

                var jobData = connection.GetJobData(parentId);
                if (jobData == null)
                {
                    // When we try to add a continuation for a removed job,
                    // the system should throw an exception instead of creating
                    // corrupted state.
                    throw new InvalidOperationException(
                              $"Can not add a continuation: parent background job '{parentId}' does not exist.");
                }

                var currentState = connection.GetStateData(parentId);

                if (currentState != null && _knownFinalStates.Contains(currentState.Name))
                {
                    var startImmediately = !awaitingState.Options.HasFlag(JobContinuationOptions.OnlyOnSucceededState) ||
                                           currentState.Name == SucceededState.StateName;

                    if (_pushResults && currentState.Data.TryGetValue("Result", out var antecedentResult))
                    {
                        context.Connection.SetJobParameter(context.BackgroundJob.Id, "AntecedentResult", antecedentResult);
                    }

                    context.CandidateState = startImmediately
                        ? awaitingState.NextState
                        : new DeletedState {
                        Reason = "Continuation condition was not met"
                    };
                }
            }
        }
Example #4
0
        public void JsonSerialize_ReturnsCorrectString_After170()
        {
            var state = new AwaitingState("parent");

            var serialized = SerializationHelper.Serialize <IState>(state, SerializationOption.TypedInternal);

            Assert.Equal(
                "{\"$type\":\"Hangfire.States.AwaitingState, Hangfire.Core\",\"ParentId\":\"parent\",\"NextState\":{\"$type\":\"Hangfire.States.EnqueuedState, Hangfire.Core\",\"Queue\":\"default\"}}",
                serialized);
        }
Example #5
0
        public string ContinueJobWith <T>(
            string parentId,
            [InstantHandle] Expression <Action <T> > action,
            JobContinuationOptions jobContinuationOptions = JobContinuationOptions.OnlyOnSucceededState,
            JobContinuationOptions atomProgress           = JobContinuationOptions.OnlyOnSucceededState)
        {
            var job       = Job.FromExpression(action);
            var nextState = new AwaitingState(parentId, new EnqueuedState(), jobContinuationOptions);

            return(CreateSubatomInternal(job, nextState, atomProgress));
        }
Example #6
0
        public string ContinueJobWith(
            string parentId,
            [InstantHandle] Expression <Func <Task> > action,
            IState state,
            JobContinuationOptions jobContinuationOptions = JobContinuationOptions.OnlyOnSucceededState,
            JobContinuationOptions atomProgress           = JobContinuationOptions.OnlyOnSucceededState)
        {
            var nextState = new AwaitingState(parentId, state, jobContinuationOptions);

            return(CreateSubatomInternal(action, nextState, atomProgress));
        }
Example #7
0
        /// <summary>
        /// Creates a new background job that will wait for another background job to be triggered.
        /// </summary>
        /// <param name="client">A job client instance.</param>
        /// <param name="parentId">Identifier of a background job to wait completion for.</param>
        /// <param name="methodCall">Method call expression that will be marshalled to a server.</param>
        /// <param name="nextState">Next state for a job, when continuation is triggered.
        /// If null, then <see cref="EnqueuedState"/> is used.</param>
        /// <param name="options">Continuation options. By default,
        /// <see cref="JobContinuationOptions.OnlyOnSucceededState"/> is used.</param>
        /// <returns>Unique identifier of a created job.</returns>
        public static string ContinueWith <T>(
            [NotNull] this IBackgroundJobClient client,
            [NotNull] string parentId,
            [NotNull, InstantHandle] Expression <Func <T, Task> > methodCall,
            [CanBeNull] IState nextState   = null,
            JobContinuationOptions options = JobContinuationOptions.OnlyOnSucceededState)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            var state = new AwaitingState(parentId, nextState ?? new EnqueuedState(), options);

            return(client.Create(Job.FromExpression(methodCall), state));
        }
Example #8
0
        /// <summary>
        /// Creates a new background job that will wait for another background job to be triggered.
        /// </summary>
        /// <param name="client">A job client instance.</param>
        /// <param name="parentId">Identifier of a background job to wait completion for.</param>
        /// <param name="methodCall">Method call expression that will be marshalled to a server.</param>
        /// <param name="nextState">Next state for a job, when continuation is triggered.</param>
        /// <param name="options">Continuation options.</param>
        /// <returns>Unique identifier of a created job.</returns>
        public static string ContinueWith <T>(
            [NotNull] this IBackgroundJobClient client,
            [NotNull] string parentId,
            [NotNull, InstantHandle] Expression <Action <T> > methodCall,
            [NotNull] IState nextState,
            JobContinuationOptions options)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            var state = new AwaitingState(parentId, nextState, options);

            return(client.Create(Job.FromExpression(methodCall), state));
        }
Example #9
0
        private void AddContinuation(ElectStateContext context, AwaitingState awaitingState)
        {
            var connection = context.Connection;
            var parentId   = awaitingState.ParentId;

            // We store continuations as a json array in a job parameter. Since there
            // is no way to add a continuation in an atomic way, we are placing a
            // distributed lock on parent job to prevent race conditions, when
            // multiple threads add continuation to the same parent job.
            using (connection.AcquireDistributedJobLock(parentId, AddJobLockTimeout))
            {
                var continuations = GetContinuations(connection, parentId);
                continuations.Add(new Continuation {
                    JobId = context.BackgroundJob.Id, Options = awaitingState.Options
                });

                var jobData = connection.GetJobData(parentId);
                if (jobData == null)
                {
                    // When we try to add a continuation for a removed job,
                    // the system should throw an exception instead of creating
                    // corrupted state.
                    throw new InvalidOperationException(
                              String.Format("Can not add a continuation: parent background job '{0}' does not exist.", parentId));
                }

                var currentState = connection.GetStateData(parentId);

                // Set continuation only after ensuring that parent job still
                // exists. Otherwise we could create add non-expiring (garbage)
                // parameter for the parent job.
                SetContinuations(connection, parentId, continuations);

                if (currentState != null && _knownFinalStates.Contains(currentState.Name))
                {
                    var startImmediately = !awaitingState.Options.HasFlag(JobContinuationOptions.OnlyOnSucceededState) ||
                                           currentState.Name == SucceededState.StateName;

                    context.CandidateState = startImmediately
                        ? awaitingState.NextState
                        : new DeletedState {
                        Reason = "Continuation condition was not met"
                    };
                }
            }
        }