public void Schedule(int startDay, int endDay, int periodDays, int jobExecutionCount, TriggerScheduleAction misfireAction, TriggerScheduleCondition condition, int timeBasisDay, bool isFirstTime, TriggerScheduleAction expectedAction, int expectedFireDay, int expectedJobExecutionCountRemaining, bool expectedIsActive, bool expectedIsFirstTime) { DateTime startTime = new DateTime(1970, 1, startDay); DateTime?endTime = endDay != 0 ? new DateTime(1970, 1, endDay) : (DateTime?)null; TimeSpan?period = periodDays != 0 ? new TimeSpan(periodDays, 0, 0, 0) : (TimeSpan?)null; int? jobExecutionCountArg = jobExecutionCount >= 0 ? jobExecutionCount : (int?)null; DateTime timeBasis = new DateTime(1970, 1, timeBasisDay); DateTime?expectedFireTime = expectedFireDay != 0 ? new DateTime(1970, 1, expectedFireDay, 0, 0, 0, DateTimeKind.Utc) : (DateTime?)null; int?expectedJobExecutionCountRemainingArg = expectedJobExecutionCountRemaining >= 0 ? expectedJobExecutionCountRemaining : (int?)null; PeriodicTrigger trigger = new PeriodicTrigger(startTime, endTime, period, jobExecutionCountArg); trigger.MisfireAction = misfireAction; trigger.MisfireThreshold = TimeSpan.Zero; trigger.IsFirstTime = expectedIsFirstTime; TriggerScheduleAction action = trigger.Schedule(condition, timeBasis, null); Assert.AreEqual(expectedAction, action); DateTimeAssert.AreEqualIncludingKind(expectedFireTime, trigger.NextFireTimeUtc); Assert.AreEqual(expectedJobExecutionCountRemainingArg, trigger.JobExecutionCountRemaining); Assert.AreEqual(expectedIsActive, trigger.IsActive); }
private void ScheduleJob(JobDetails jobDetails) { DateTime timeBasis = DateTime.UtcNow; TriggerScheduleAction action = UpdateTrigger(timeBasis, jobDetails); PerformActionAndSaveChanges(timeBasis, jobDetails, action); }
/// <summary> /// Creates a periodic trigger. /// </summary> /// <param name="startTimeUtc">The UTC date and time when the trigger will first fire</param> /// <param name="endTimeUtc">The UTC date and time when the trigger must stop firing. /// If the time is set to null, the trigger may continue firing indefinitely.</param> /// <param name="period">The recurrence period of the trigger. /// If the period is set to null, the trigger will fire exactly once /// and never recur.</param> /// <param name="jobExecutionCount">The number of job executions remaining before the trigger /// stops firing. This number is decremented each time the job executes /// until it reaches zero. If the count is set to null, the number of times the job /// may execute is unlimited.</param> /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="period"/> is negative or zero</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="jobExecutionCount"/> is negative</exception> public PeriodicTrigger(DateTime startTimeUtc, DateTime? endTimeUtc, TimeSpan? period, int? jobExecutionCount) { if (period.HasValue && period.Value.Ticks <= 0) throw new ArgumentOutOfRangeException("period", "The recurrence period must not be negative or zero."); if (jobExecutionCount.HasValue && jobExecutionCount.Value < 0) throw new ArgumentOutOfRangeException("jobExecutionCount", "The job execution count remaining must not be negative."); this.startTimeUtc = DateTimeUtils.AssumeUniversalTime(startTimeUtc); this.endTimeUtc = DateTimeUtils.AssumeUniversalTime(endTimeUtc); this.period = period; jobExecutionCountRemaining = jobExecutionCount; isFirstTime = true; misfireAction = DefaultMisfireAction; }
public void Schedule(string utcFireTime, TriggerScheduleAction expectedAction, int expectedRemainingJobCount) { var startDate = new DateTime(2000, 3, 14); DateTime?endDate = new DateTime(2001, 3, 14); var dailyFireWindow = new DailyFireWindow(3, 16); TimeSpan?interval = new TimeSpan(0, 1, 0); int? jobExecutionCount = 33; var trigger = new DailyPeriodicWindowTrigger(startDate, endDate, dailyFireWindow, interval, jobExecutionCount); var triggerScheduleAction = trigger.Schedule(TriggerScheduleCondition.Fire, DateTime.Parse(utcFireTime), null); Assert.AreEqual(expectedAction, triggerScheduleAction); Assert.AreEqual(expectedRemainingJobCount, trigger.JobExecutionCountRemaining); }
/// <summary> /// Creates a periodic trigger. /// </summary> /// <param name="startTimeUtc">The UTC date and time when the trigger will first fire</param> /// <param name="endTimeUtc">The UTC date and time when the trigger must stop firing. /// If the time is set to null, the trigger may continue firing indefinitely.</param> /// <param name="period">The recurrence period of the trigger. /// If the period is set to null, the trigger will fire exactly once /// and never recur.</param> /// <param name="jobExecutionCount">The number of job executions remaining before the trigger /// stops firing. This number is decremented each time the job executes /// until it reaches zero. If the count is set to null, the number of times the job /// may execute is unlimited.</param> /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="period"/> is negative or zero</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="jobExecutionCount"/> is negative</exception> public PeriodicTrigger(DateTime startTimeUtc, DateTime?endTimeUtc, TimeSpan?period, int?jobExecutionCount) { if (period.HasValue && period.Value.Ticks <= 0) { throw new ArgumentOutOfRangeException("period", "The recurrence period must not be negative or zero."); } if (jobExecutionCount.HasValue && jobExecutionCount.Value < 0) { throw new ArgumentOutOfRangeException("jobExecutionCount", "The job execution count remaining must not be negative."); } this.startTimeUtc = DateTimeUtils.AssumeUniversalTime(startTimeUtc); this.endTimeUtc = DateTimeUtils.AssumeUniversalTime(endTimeUtc); this.period = period; jobExecutionCountRemaining = jobExecutionCount; isFirstTime = true; misfireAction = DefaultMisfireAction; }
internal override TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc) { if (action != TriggerScheduleAction.ExecuteJob) { return(base.ScheduleSuggestedAction(action, timeBasisUtc)); } if (CannotExecuteAgain() || EndTimeHasPassed(timeBasisUtc)) { return(StopAction()); } if (!StartTimeHasPassed(timeBasisUtc) || !InDailyFireWindow(timeBasisUtc)) { return(SkipAction()); } return(ExecuteAction()); }
private void PerformActionAndSaveChanges(DateTime timeBasis, JobDetails jobDetails, TriggerScheduleAction action) { try { switch (action) { case TriggerScheduleAction.Skip: jobDetails.JobState = JobState.Scheduled; jobStore.SaveJobDetails(jobDetails); return; case TriggerScheduleAction.DeleteJob: jobStore.DeleteJob(jobDetails.JobSpec.Name); return; case TriggerScheduleAction.ExecuteJob: jobDetails.JobState = JobState.Running; jobDetails.LastJobExecutionDetails = new JobExecutionDetails(guid, timeBasis); jobDetails.LastJobExecutionDetails.StatusMessage = "Running."; jobStore.SaveJobDetails(jobDetails); BeginExecuteJob(jobDetails); return; case TriggerScheduleAction.Stop: break; default: logger.WarnFormat("Trigger for job '{0}' returned an unexpected action '{1}'. The trigger will be stopped.", jobDetails.JobSpec.Name, action); break; } jobDetails.JobState = JobState.Stopped; jobDetails.NextTriggerFireTimeUtc = null; jobDetails.NextTriggerMisfireThreshold = null; jobStore.SaveJobDetails(jobDetails); } catch (ConcurrentModificationException ex) { logger.DebugFormat(ex, "Job '{0}' was concurrently modified by another scheduler instance so the changes made " + "by this scheduler instance will be discarded. This is normal behavior in a cluster.", jobDetails.JobSpec.Name); } }
[TestCase((TriggerScheduleAction)9999)] // invalid trigger action public void ScheduleHandlesUnexpectedActionReceivedFromTriggerByStoppingTheTrigger(TriggerScheduleAction action) { JobDetails jobDetails = new JobDetails(dummyJobSpec, DateTime.UtcNow); jobDetails.JobState = JobState.Pending; PrepareMockJobWatcher(jobDetails); Expect.Call(mockTrigger.Schedule(TriggerScheduleCondition.Latch, DateTime.UtcNow, null)) .Constraints(Rhino.Mocks.Constraints.Is.Equal(TriggerScheduleCondition.Latch), Rhino.Mocks.Constraints.Is.Anything(), Rhino.Mocks.Constraints.Is.Null()) .Return(action); Expect.Call(mockTrigger.NextFireTimeUtc).Return(new DateTime(1970, 1, 5, 0, 0, 0, DateTimeKind.Utc)); Expect.Call(mockTrigger.NextMisfireThreshold).Return(new TimeSpan(0, 1, 0)); mockJobStore.SaveJobDetails(jobDetails); LastCall.Do((SaveJobDetailsDelegate)WakeOnSaveJobDetails); Mocks.ReplayAll(); RunSchedulerUntilWake(); Assert.AreEqual(JobState.Stopped, jobDetails.JobState); Assert.IsNull(jobDetails.NextTriggerFireTimeUtc); Assert.IsNull(jobDetails.NextTriggerMisfireThreshold); Assert.IsNull(jobDetails.LastJobExecutionDetails); Assert.IsNull(jobDetails.JobSpec.JobData); }
public void ScheduleHandlesUnexpectedActionReceivedFromTriggerByStoppingTheTrigger(TriggerScheduleAction action) { JobDetails jobDetails = new JobDetails(dummyJobSpec, DateTime.UtcNow); jobDetails.JobState = JobState.Pending; PrepareMockJobWatcher(jobDetails); Expect.Call(mockTrigger.Schedule(TriggerScheduleCondition.Latch, DateTime.UtcNow, null)) .Constraints(Rhino.Mocks.Constraints.Is.Equal(TriggerScheduleCondition.Latch), Rhino.Mocks.Constraints.Is.Anything(), Rhino.Mocks.Constraints.Is.Null()) .Return(action); Expect.Call(mockTrigger.NextFireTimeUtc).Return(new DateTime(1970, 1, 5, 0, 0, 0, DateTimeKind.Utc)); Expect.Call(mockTrigger.NextMisfireThreshold).Return(new TimeSpan(0, 1, 0)); mockJobStore.SaveJobDetails(jobDetails); LastCall.Do((SaveJobDetailsDelegate) WakeOnSaveJobDetails); Mocks.ReplayAll(); RunSchedulerUntilWake(); Assert.AreEqual(JobState.Stopped, jobDetails.JobState); Assert.IsNull(jobDetails.NextTriggerFireTimeUtc); Assert.IsNull(jobDetails.NextTriggerMisfireThreshold); Assert.IsNull(jobDetails.LastJobExecutionDetails); Assert.IsNull(jobDetails.JobSpec.JobData); }
private TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc) { switch (action) { case TriggerScheduleAction.Stop: break; case TriggerScheduleAction.ExecuteJob: // If the job cannot execute again then stop. if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0) { break; } // If the end time has passed then stop. if (endTimeUtc.HasValue && timeBasisUtc > endTimeUtc.Value) { break; } // If the start time is still in the future then hold off until then. if (timeBasisUtc < startTimeUtc) { nextFireTimeUtc = startTimeUtc; return(TriggerScheduleAction.Skip); } // Otherwise execute the job. nextFireTimeUtc = null; jobExecutionCountRemaining -= 1; return(TriggerScheduleAction.ExecuteJob); case TriggerScheduleAction.DeleteJob: nextFireTimeUtc = null; jobExecutionCountRemaining = 0; return(TriggerScheduleAction.DeleteJob); case TriggerScheduleAction.Skip: // If the job cannot execute again then stop. if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0) { break; } // If the start time is still in the future then hold off until then. if (isFirstTime || timeBasisUtc < startTimeUtc) { nextFireTimeUtc = startTimeUtc; return(TriggerScheduleAction.Skip); } // If the trigger is not periodic then we must be skipping the only chance the // job had to run so stop the trigger. if (!period.HasValue) { break; } // Compute when the next occurrence should be. TimeSpan timeSinceStart = timeBasisUtc - startTimeUtc; TimeSpan timeSinceLastPeriod = new TimeSpan(timeSinceStart.Ticks % period.Value.Ticks); nextFireTimeUtc = timeBasisUtc + period - timeSinceLastPeriod; // If the next occurrence is past the end time then stop. if (nextFireTimeUtc > endTimeUtc) { break; } // Otherwise we're good. return(TriggerScheduleAction.Skip); } // Stop the trigger. nextFireTimeUtc = null; jobExecutionCountRemaining = 0; return(TriggerScheduleAction.Stop); }
private TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc) { switch (action) { case TriggerScheduleAction.Stop: break; case TriggerScheduleAction.ExecuteJob: // If the job cannot execute again then stop. if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0) break; // If the end time has passed then stop. if (endTimeUtc.HasValue && timeBasisUtc > endTimeUtc.Value) break; // If the start time is still in the future then hold off until then. if (timeBasisUtc < startTimeUtc) { nextFireTimeUtc = startTimeUtc; return TriggerScheduleAction.Skip; } // Otherwise execute the job. nextFireTimeUtc = null; jobExecutionCountRemaining -= 1; return TriggerScheduleAction.ExecuteJob; case TriggerScheduleAction.DeleteJob: nextFireTimeUtc = null; jobExecutionCountRemaining = 0; return TriggerScheduleAction.DeleteJob; case TriggerScheduleAction.Skip: // If the job cannot execute again then stop. if (jobExecutionCountRemaining.HasValue && jobExecutionCountRemaining.Value <= 0) break; // If the start time is still in the future then hold off until then. if (isFirstTime || timeBasisUtc < startTimeUtc) { nextFireTimeUtc = startTimeUtc; return TriggerScheduleAction.Skip; } // If the trigger is not periodic then we must be skipping the only chance the // job had to run so stop the trigger. if (! period.HasValue) break; // Compute when the next occurrence should be. TimeSpan timeSinceStart = timeBasisUtc - startTimeUtc; TimeSpan timeSinceLastPeriod = new TimeSpan(timeSinceStart.Ticks%period.Value.Ticks); nextFireTimeUtc = timeBasisUtc + period - timeSinceLastPeriod; // If the next occurrence is past the end time then stop. if (nextFireTimeUtc > endTimeUtc) break; // Otherwise we're good. return TriggerScheduleAction.Skip; } // Stop the trigger. nextFireTimeUtc = null; jobExecutionCountRemaining = 0; return TriggerScheduleAction.Stop; }
public void Schedule(int startDay, int endDay, int periodDays, int jobExecutionCount, TriggerScheduleAction misfireAction, TriggerScheduleCondition condition, int timeBasisDay, bool isFirstTime, TriggerScheduleAction expectedAction, int expectedFireDay, int expectedJobExecutionCountRemaining, bool expectedIsActive, bool expectedIsFirstTime) { DateTime startTime = new DateTime(1970, 1, startDay); DateTime? endTime = endDay != 0 ? new DateTime(1970, 1, endDay) : (DateTime?) null; TimeSpan? period = periodDays != 0 ? new TimeSpan(periodDays, 0, 0, 0) : (TimeSpan?) null; int? jobExecutionCountArg = jobExecutionCount >= 0 ? jobExecutionCount : (int?) null; DateTime timeBasis = new DateTime(1970, 1, timeBasisDay); DateTime? expectedFireTime = expectedFireDay != 0 ? new DateTime(1970, 1, expectedFireDay, 0, 0, 0, DateTimeKind.Utc) : (DateTime?) null; int? expectedJobExecutionCountRemainingArg = expectedJobExecutionCountRemaining >= 0 ? expectedJobExecutionCountRemaining : (int?) null; PeriodicTrigger trigger = new PeriodicTrigger(startTime, endTime, period, jobExecutionCountArg); trigger.MisfireAction = misfireAction; trigger.MisfireThreshold = TimeSpan.Zero; trigger.IsFirstTime = expectedIsFirstTime; TriggerScheduleAction action = trigger.Schedule(condition, timeBasis, null); Assert.AreEqual(expectedAction, action); DateTimeAssert.AreEqualIncludingKind(expectedFireTime, trigger.NextFireTimeUtc); Assert.AreEqual(expectedJobExecutionCountRemainingArg, trigger.JobExecutionCountRemaining); Assert.AreEqual(expectedIsActive, trigger.IsActive); }
internal override TriggerScheduleAction ScheduleSuggestedAction(TriggerScheduleAction action, DateTime timeBasisUtc) { if (action != TriggerScheduleAction.ExecuteJob) return base.ScheduleSuggestedAction(action, timeBasisUtc); if (CannotExecuteAgain() || EndTimeHasPassed(timeBasisUtc)) return StopAction(); if (!StartTimeHasPassed(timeBasisUtc) || !InDailyFireWindow(timeBasisUtc)) return SkipAction(); return ExecuteAction(); }
public void Schedule(string utcFireTime, TriggerScheduleAction expectedAction, int expectedRemainingJobCount) { var startDate = new DateTime(2000, 3, 14); DateTime? endDate = new DateTime(2001, 3, 14); var dailyFireWindow = new DailyFireWindow(3, 16); TimeSpan? interval = new TimeSpan(0, 1, 0); int? jobExecutionCount = 33; var trigger = new DailyPeriodicWindowTrigger(startDate, endDate, dailyFireWindow, interval, jobExecutionCount); var triggerScheduleAction = trigger.Schedule(TriggerScheduleCondition.Fire, DateTime.Parse(utcFireTime), null); Assert.AreEqual(expectedAction, triggerScheduleAction); Assert.AreEqual(expectedRemainingJobCount, trigger.JobExecutionCountRemaining); }