public static void GetCreatedAt_ReturnsNull_AwaitingState() { var state = new AwaitingState("1"); var result = state.GetCreatedAt(); Assert.Null(result); }
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" }; } } }
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); }
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)); }
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)); }
/// <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)); }
/// <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)); }
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" }; } } }