public async Task ClockSkew_IsNotCalculatedPastDue() { // First, invoke a function with clock skew. This will store the next status back in the // 'updatedStatus' variable. CreateTestListener("0 0 0 * * *"); var status = new ScheduleStatus { Last = new DateTime(2016, 3, 4), Next = new DateTime(2016, 3, 5), LastUpdated = new DateTime(2016, 3, 4) }; DateTime invocationTime = status.Next.AddMilliseconds(-1); ScheduleStatus updatedStatus = null; _mockScheduleMonitor.Setup(p => p.UpdateStatusAsync(_testTimerName, It.IsAny <ScheduleStatus>())) .Callback <string, ScheduleStatus>((n, s) => updatedStatus = s) .Returns(Task.FromResult(true)); _listener.ScheduleStatus = status; await _listener.InvokeJobFunction(invocationTime, isPastDue : false, runOnStartup : false); _listener.Dispose(); // Now, use that status variable to calculate past due (this ultimately calls the base class implementation). // This ensures we do not consider clock skewed functions as past due -- this was previously a bug. // Use a new mock monitor so we can CallBase on it without affecting the class-level one. var mockMonitor = new Mock <ScheduleMonitor>(); mockMonitor.CallBase = true; DateTime hostStartTime = new DateTime(2016, 3, 5, 1, 0, 0); TimeSpan pastDue = await mockMonitor.Object.CheckPastDueAsync(_testTimerName, hostStartTime, _schedule, updatedStatus); Assert.Equal(TimeSpan.Zero, pastDue); _mockScheduleMonitor.VerifyAll(); }
private async Task CheckPastDue_ScheduleChange_Longer(bool lastSet, bool lastUpdatedSet) { DateTime now = DateTime.Parse("1/1/2017 9:35"); ScheduleStatus status = new ScheduleStatus { Last = lastSet ? DateTime.Parse("1/1/2017 9:00") : default(DateTime), Next = DateTime.Parse("1/1/2017 10:00"), LastUpdated = lastUpdatedSet ? DateTime.Parse("1/1/2017 9:00") : default(DateTime) }; MockScheduleMonitor monitor = new MockScheduleMonitor(); // change to daily schedule (status is hourly) TimeSpan pastDueAmount = await monitor.CheckPastDueAsync(_timerName, now, _dailySchedule, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); DateTime expectedNext = DateTime.Parse("1/2/2017 0:00"); Assert.Equal(default(DateTime), monitor.CurrentStatus.Last); Assert.Equal(expectedNext, monitor.CurrentStatus.Next); if (lastUpdatedSet || lastSet) { Assert.Equal(DateTime.Parse("1/1/2017 9:00"), monitor.CurrentStatus.LastUpdated); } else { // Legacy behavior -- before 'LastUpdated' was added. Assert.Equal(now, monitor.CurrentStatus.LastUpdated); } }
public async Task CheckPastDue_NowPastNext(bool lastSet, bool lastUpdatedSet) { // Move the time 1 second ahead of 'Next'. We should catch this as past due. DateTime now = DateTime.Parse("1/1/2017 10:00:01"); ScheduleStatus status = new ScheduleStatus { Last = lastSet ? DateTime.Parse("1/1/2017 9:00") : default(DateTime), Next = DateTime.Parse("1/1/2017 10:00"), LastUpdated = lastUpdatedSet ? DateTime.Parse("1/1/2017 9:00") : default(DateTime) }; MockScheduleMonitor monitor = new MockScheduleMonitor(); TimeSpan pastDueAmount = await monitor.CheckPastDueAsync(_timerName, now, _hourlySchedule, status); if (lastUpdatedSet || lastSet) { Assert.Equal(TimeSpan.FromSeconds(1), pastDueAmount); Assert.Null(monitor.CurrentStatus); } else { // Legacy behavior -- 'LastUpdated' fixed this. The schedule didn't change and we're past due, // but we miss it because there is no 'Last' value, which we require to calculate the 'Next' // value. It also shouldn't register as a schedule change. Assert.Equal(TimeSpan.Zero, pastDueAmount); Assert.Equal(default(DateTime), monitor.CurrentStatus.Last); Assert.Equal(DateTime.Parse("1/1/2017 11:00"), monitor.CurrentStatus.Next); Assert.Equal(now, monitor.CurrentStatus.LastUpdated); } }
public async Task CheckPastDueAsync_ReturnsExpectedResult() { Mock <TimerSchedule> mockSchedule = new Mock <TimerSchedule>(MockBehavior.Strict); DateTime now = DateTime.Now; DateTime next = now + TimeSpan.FromDays(1); ScheduleStatus status = new ScheduleStatus { Last = now, Next = next }; mockSchedule.Setup(p => p.GetNextOccurrence(It.IsAny <DateTime>())).Returns(next); TimeSpan pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); now = now + TimeSpan.FromHours(23); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); now = now + TimeSpan.FromHours(1); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); now = now + TimeSpan.FromHours(1); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.FromHours(1), pastDueAmount); }
public async Task UpdateAsync_WritesStatusToFile() { DateTime now = DateTime.Now; DateTime expectedNext = DateTime.Now + TimeSpan.FromMinutes(1); File.Delete(_statusFile); Assert.False(File.Exists(_statusFile)); ScheduleStatus status = new ScheduleStatus { Last = now, Next = expectedNext }; await _monitor.UpdateStatusAsync(_testTimerName, status); Assert.True(File.Exists(_statusFile)); VerifyScheduleStatus(now, expectedNext); now = expectedNext; expectedNext = now + TimeSpan.FromMinutes(1); status = new ScheduleStatus { Last = now, Next = expectedNext }; await _monitor.UpdateStatusAsync(_testTimerName, status); VerifyScheduleStatus(now, expectedNext); }
public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); if (_timer != null && _timer.Enabled) { throw new InvalidOperationException("The listener has already been started."); } // if schedule monitoring is enabled, record (or initialize) // the current schedule status bool isPastDue = false; // we use DateTime.Now rather than DateTime.UtcNow to allow the local machine to set the time zone. In Azure this will be // UTC by default, but can be configured to use any time zone if it makes scheduling easier. DateTime now = DateTime.Now; _logger.LogDebug($"The '{_functionShortName}' timer is using the schedule '{_schedule.ToString()}' and the local time zone: '{TimeZoneInfo.Local.DisplayName}'"); if (ScheduleMonitor != null) { // check to see if we've missed an occurrence since we last started. // If we have, invoke it immediately. ScheduleStatus = await ScheduleMonitor.GetStatusAsync(_timerLookupName); _logger.LogDebug($"Function '{_functionShortName}' initial status: Last='{ScheduleStatus?.Last.ToString("o")}', Next='{ScheduleStatus?.Next.ToString("o")}', LastUpdated='{ScheduleStatus?.LastUpdated.ToString("o")}'"); TimeSpan pastDueDuration = await ScheduleMonitor.CheckPastDueAsync(_timerLookupName, now, _schedule, ScheduleStatus); isPastDue = pastDueDuration != TimeSpan.Zero; } if (ScheduleStatus == null) { // no schedule status has been stored yet, so initialize ScheduleStatus = new ScheduleStatus { Last = default(DateTime), Next = _schedule.GetNextOccurrence(now) }; } if (isPastDue) { _logger.LogDebug($"Function '{_functionShortName}' is past due on startup. Executing now."); await InvokeJobFunction(now, isPastDue : true, originalSchedule : ScheduleStatus.Next); } else if (_attribute.RunOnStartup) { // The job is configured to run immediately on startup _logger.LogDebug($"Function '{_functionShortName}' is configured to run on startup. Executing now."); await InvokeJobFunction(now, runOnStartup : true); } // log the next several occurrences to console for visibility string nextOccurrences = TimerInfo.FormatNextOccurrences(_schedule, 5, functionShortName: _functionShortName); _logger.LogInformation(nextOccurrences); StartTimer(DateTime.Now); }
public ScheduleOverviewResource( int scheduleId, string wellName, string @operator, DateTime fracStartDate, DateTime fracEndDate, int duration, string api, double surfaceLat, double surfaceLong, double bottomholeLat, double bottomholeLong, string tvd, int?startIn, ScheduleStatus status) { this.ScheduleId = scheduleId; this.WellName = wellName; this.Operator = @operator; this.FracStartDate = fracStartDate; this.FracEndDate = fracEndDate; this.Duration = duration; this.Api = api; this.SurfaceLat = surfaceLat; this.SurfaceLong = surfaceLong; this.BottomholeLat = bottomholeLat; this.BottomholeLong = bottomholeLong; this.Tvd = tvd; this.StartIn = startIn; this.Status = status; }
public async Task Timer_CannotHaveNegativeInterval() { CreateTestListener("* * * * * *", useMonitor: true); ScheduleStatus status = new ScheduleStatus(); _mockScheduleMonitor.Setup(p => p.GetStatusAsync(_testTimerName)).ReturnsAsync(status); // Make sure we invoke b/c we're past due. _mockScheduleMonitor.Setup(p => p.CheckPastDueAsync(_testTimerName, It.IsAny <DateTime>(), It.IsAny <TimerSchedule>(), status)) .ReturnsAsync(TimeSpan.FromMilliseconds(1)); // Use the monitor to sleep for a second. This ensures that we recalculate the Next value before // starting the timer. Otherwise, you can end up with a negative interval. bool updateCalled = false; _mockScheduleMonitor.Setup(p => p.UpdateStatusAsync(_testTimerName, It.IsAny <ScheduleStatus>())) .Callback(() => { // only sleep for the first call if (!updateCalled) { Thread.Sleep(1000); } updateCalled = true; }) .Returns(Task.FromResult(true)); await _listener.StartAsync(CancellationToken.None); Assert.True(updateCalled); }
internal async Task InvokeJobFunction(DateTime lastOccurrence, bool isPastDue = false) { CancellationToken token = _cancellationTokenSource.Token; TimerInfo timerInfo = new TimerInfo(_schedule, _scheduleStatus, isPastDue); TriggeredFunctionData input = new TriggeredFunctionData { TriggerValue = timerInfo }; try { FunctionResult result = await _executor.TryExecuteAsync(input, token); if (!result.Succeeded) { token.ThrowIfCancellationRequested(); } } catch { // We don't want any function errors to stop the execution // schedule. Errors will be logged to Dashboard already. } if (ScheduleMonitor != null) { _scheduleStatus = new ScheduleStatus { Last = lastOccurrence, Next = _schedule.GetNextOccurrence(lastOccurrence) }; await ScheduleMonitor.UpdateStatusAsync(_timerName, _scheduleStatus); } }
public async Task InvokeJobFunction_UpdatesScheduleMonitor_AccountsForSkew(string schedule, bool useMonitor) { CreateTestListener(schedule, useMonitor); var status = new ScheduleStatus { Last = new DateTime(2016, 3, 4), Next = new DateTime(2016, 3, 5) }; // Run the function 1 millisecond before it's next scheduled run. DateTime invocationTime = status.Next.AddMilliseconds(-1); // It should not use the same 'Next' value twice in a row. DateTime expectedNextOccurrence = new DateTime(2016, 3, 6); bool monitorCalled = false; _mockScheduleMonitor.Setup(p => p.UpdateStatusAsync(_testTimerName, It.Is <ScheduleStatus>(q => q.Last == status.Next && q.Next == expectedNextOccurrence))) .Callback(() => monitorCalled = true) .Returns(Task.FromResult(true)); // Initialize the _scheduleStatus _listener.ScheduleStatus = status; await _listener.InvokeJobFunction(invocationTime, isPastDue : false, runOnStartup : false); _listener.Dispose(); Assert.Equal(status.Next, _listener.ScheduleStatus.Last); Assert.Equal(expectedNextOccurrence, _listener.ScheduleStatus.Next); Assert.Equal(monitorCalled, useMonitor); }
public async Task UpdateStatusAsync_MultipleFunctions() { // update status for 3 functions ScheduleStatus expected = new ScheduleStatus { Last = DateTime.Now.Subtract(TimeSpan.FromMinutes(5)), Next = DateTime.Now.AddMinutes(5) }; for (int i = 0; i < 3; i++) { await _scheduleMonitor.UpdateStatusAsync(TestTimerName + i.ToString(), expected); } var segments = await _scheduleMonitor.TimerStatusDirectory.ListBlobsSegmentedAsync( useFlatBlobListing : true, blobListingDetails : BlobListingDetails.None, maxResults : null, currentToken : null, options : null, operationContext : null); var statuses = segments.Results.Cast <CloudBlockBlob>().ToArray(); Assert.Equal(3, statuses.Length); Assert.Equal("timers/testhostid/TestProgram.TestTimer0/status", statuses[0].Name); Assert.Equal("timers/testhostid/TestProgram.TestTimer1/status", statuses[1].Name); Assert.Equal("timers/testhostid/TestProgram.TestTimer2/status", statuses[2].Name); }
public async Task RunInitialStatusTestAsync(ScheduleStatus initialStatus, string expected) { _mockScheduleMonitor .Setup(m => m.GetStatusAsync(_testTimerName)) .ReturnsAsync(initialStatus); _mockScheduleMonitor .Setup(m => m.CheckPastDueAsync(_testTimerName, It.IsAny <DateTime>(), _schedule, It.IsAny <ScheduleStatus>())) .ReturnsAsync(TimeSpan.Zero); await _listener.StartAsync(CancellationToken.None); await _listener.StopAsync(CancellationToken.None); _listener.Dispose(); LogMessage[] verboseTraces = _logger.GetLogMessages() .Where(m => m.Level == LogLevel.Debug) .OrderBy(t => t.Timestamp) .ToArray(); Assert.Equal(3, verboseTraces.Length); Assert.Contains("timer is using the schedule 'Cron: '0 * * * * *'' and the local time zone:", verboseTraces[0].FormattedMessage); Assert.Equal(expected, verboseTraces[1].FormattedMessage); Assert.Contains($"Timer for '{_functionShortName}' started with interval", verboseTraces[2].FormattedMessage); }
public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); if (_timer != null && _timer.Enabled) { throw new InvalidOperationException("The listener has already been started."); } // if schedule monitoring is enabled, record (or initialize) // the current schedule status bool isPastDue = false; DateTime now = DateTime.Now; if (ScheduleMonitor != null) { // check to see if we've missed an occurrence since we last started. // If we have, invoke it immediately. _scheduleStatus = await ScheduleMonitor.GetStatusAsync(_timerName); TimeSpan pastDueDuration = await ScheduleMonitor.CheckPastDueAsync(_timerName, now, _schedule, _scheduleStatus); isPastDue = pastDueDuration != TimeSpan.Zero; if (_scheduleStatus == null) { // no schedule status has been stored yet, so initialize _scheduleStatus = new ScheduleStatus { Last = default(DateTime), Next = _schedule.GetNextOccurrence(now) }; } } if (isPastDue) { _trace.Verbose(string.Format("Function '{0}' is past due on startup. Executing now.", _timerName)); await InvokeJobFunction(now, true); } else if (_attribute.RunOnStartup) { // The job is configured to run immediately on startup _trace.Verbose(string.Format("Function '{0}' is configured to run on startup. Executing now.", _timerName)); await InvokeJobFunction(now); } // log the next several occurrences to console for visibility string nextOccurrences = TimerInfo.FormatNextOccurrences(_schedule, 5); _trace.Verbose(nextOccurrences); // start the timer now = DateTime.Now; DateTime nextOccurrence = _schedule.GetNextOccurrence(now); TimeSpan nextInterval = nextOccurrence - now; StartTimer(nextInterval); }
public void Resume() { if (this.Scheduler.IsStarted) { this.Scheduler.PauseAll(); this.GetScheduleStatus = ScheduleStatus.Starting; } }
public void Pause() { if (this.Scheduler.IsStarted) { this.Scheduler.PauseAll(); this.GetScheduleStatus = ScheduleStatus.Pause; } }
private static string Linethrough(ScheduleStatus status) { if (status == ScheduleStatus.Removed) { return("text-decoration: line-through;"); } return(String.Empty); }
public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); if (_timer != null && _timer.Enabled) { throw new InvalidOperationException("The listener has already been started."); } // if schedule monitoring is enabled, record (or initialize) // the current schedule status bool isPastDue = false; // we use DateTime.Now rather than DateTime.UtcNow to allow the local machine to set the time zone. In Azure this will be // UTC by default, but can be configured to use any time zone if it makes scheduling easier. DateTime now = DateTime.Now; if (ScheduleMonitor != null) { // check to see if we've missed an occurrence since we last started. // If we have, invoke it immediately. ScheduleStatus = await ScheduleMonitor.GetStatusAsync(_timerName); TimeSpan pastDueDuration = await ScheduleMonitor.CheckPastDueAsync(_timerName, now, _schedule, ScheduleStatus); isPastDue = pastDueDuration != TimeSpan.Zero; } if (ScheduleStatus == null) { // no schedule status has been stored yet, so initialize ScheduleStatus = new ScheduleStatus { Last = default(DateTime), Next = _schedule.GetNextOccurrence(now) }; } if (isPastDue) { _trace.Verbose(string.Format("Function '{0}' is past due on startup. Executing now.", _timerName)); await InvokeJobFunction(now, isPastDue : true); } else if (_attribute.RunOnStartup) { // The job is configured to run immediately on startup _trace.Verbose(string.Format("Function '{0}' is configured to run on startup. Executing now.", _timerName)); await InvokeJobFunction(now, runOnStartup : true); } // log the next several occurrences to console for visibility string nextOccurrences = TimerInfo.FormatNextOccurrences(_schedule, 5); _trace.Info(nextOccurrences); StartTimer(DateTime.Now); }
/// <summary> /// Invokes the job function. /// </summary> /// <param name="invocationTime">The time of the invocation, likely DateTime.Now.</param> /// <param name="isPastDue">True if the invocation is because the invocation is due to a past due timer.</param> /// <param name="runOnStartup">True if the invocation is because the timer is configured to run on startup.</param> internal async Task InvokeJobFunction(DateTime invocationTime, bool isPastDue = false, bool runOnStartup = false) { CancellationToken token = _cancellationTokenSource.Token; ScheduleStatus timerInfoStatus = null; if (ScheduleMonitor != null) { timerInfoStatus = ScheduleStatus; } TimerInfo timerInfo = new TimerInfo(_schedule, timerInfoStatus, isPastDue); TriggeredFunctionData input = new TriggeredFunctionData { TriggerValue = timerInfo }; try { FunctionResult result = await _executor.TryExecuteAsync(input, token); if (!result.Succeeded) { token.ThrowIfCancellationRequested(); } } catch { // We don't want any function errors to stop the execution // schedule. Errors will be logged to Dashboard already. } // If the trigger fired before it was officially scheduled (likely under 1 second due to clock skew), // adjust the invocation time forward for the purposes of calculating the next occurrence. // Without this, it's possible to set the 'Next' value to the same time twice in a row, // which results in duplicate triggers if the site restarts. DateTime adjustedInvocationTime = invocationTime; if (!isPastDue && !runOnStartup && ScheduleStatus?.Next > invocationTime) { adjustedInvocationTime = ScheduleStatus.Next; } // Create the Last value with the adjustedInvocationTime; otherwise, the listener will // consider this a schedule change when the host next starts. ScheduleStatus = new ScheduleStatus { Last = adjustedInvocationTime, Next = _schedule.GetNextOccurrence(adjustedInvocationTime), LastUpdated = adjustedInvocationTime }; if (ScheduleMonitor != null) { await ScheduleMonitor.UpdateStatusAsync(_timerName, ScheduleStatus); _logger.LogDebug($"Function '{_timerName}' updated status: Last='{ScheduleStatus.Last.ToString("o")}', Next='{ScheduleStatus.Next.ToString("o")}', LastUpdated='{ScheduleStatus.LastUpdated.ToString("o")}'"); } }
public void Start() { if (this.Scheduler.InStandbyMode || !this.Scheduler.IsStarted) { this.Scheduler.Start(); this.Scheduler.Clear(); this.GetScheduleStatus = ScheduleStatus.Starting; } }
public void Stop() { if (this.Scheduler.IsStarted) { this.Scheduler.Standby(); QuartzTaskList.Clear(); //QuartzModelList.Clear(); this.GetScheduleStatus = ScheduleStatus.Stop; } }
/// <summary> /// Initializes a new instance of the BackupSchedule class. /// </summary> /// <param name="scheduleRecurrence">The schedule recurrence.</param> /// <param name="backupType">The type of backup which needs to be /// taken. Possible values include: 'LocalSnapshot', /// 'CloudSnapshot'</param> /// <param name="retentionCount">The number of backups to be /// retained.</param> /// <param name="startTime">The start time of the schedule.</param> /// <param name="scheduleStatus">The schedule status. Possible values /// include: 'Enabled', 'Disabled'</param> /// <param name="id">The path ID that uniquely identifies the /// object.</param> /// <param name="name">The name of the object.</param> /// <param name="type">The hierarchical type of the object.</param> /// <param name="kind">The Kind of the object. Currently only /// Series8000 is supported. Possible values include: /// 'Series8000'</param> /// <param name="lastSuccessfulRun">The last successful backup run /// which was triggered for the schedule.</param> public BackupSchedule(ScheduleRecurrence scheduleRecurrence, BackupType backupType, long retentionCount, System.DateTime startTime, ScheduleStatus scheduleStatus, string id = default(string), string name = default(string), string type = default(string), Kind?kind = default(Kind?), System.DateTime?lastSuccessfulRun = default(System.DateTime?)) : base(id, name, type, kind) { ScheduleRecurrence = scheduleRecurrence; BackupType = backupType; RetentionCount = retentionCount; StartTime = startTime; ScheduleStatus = scheduleStatus; LastSuccessfulRun = lastSuccessfulRun; }
public List <ScheduleStatus> ListScheduleStatuses() { ScheduleStatus emptyScheduleStatus = new ScheduleStatus(); emptyScheduleStatus.ScheduleStatusId = 0; emptyScheduleStatus.ScheduleStatusName = "Select Schedule Status"; List <ScheduleStatus> listScheduleStatus = repository.ScheduleStatuses.OrderBy(x => x.ScheduleStatusName).ToList(); listScheduleStatus.Insert(0, emptyScheduleStatus); return(listScheduleStatus); }
public async Task Listener_LogsInitialStatus_WhenUsingMonitor() { var status = new ScheduleStatus { Last = new DateTime(2016, 3, 4), Next = new DateTime(2016, 3, 4, 0, 0, 1) }; var expected = $"Function 'Program.TestTimerJob' initial status: Last='{status.Last.ToString("o")}', Next='{status.Next.ToString("o")}'"; await RunInitialStatusTestAsync(status, expected); }
public void ScheduleStatus_ReturnsExpectedValue() { TimerSchedule schedule = new ConstantSchedule(TimeSpan.FromDays(1)); TimerInfo timerInfo = new TimerInfo(schedule, null); Assert.Null(timerInfo.ScheduleStatus); ScheduleStatus scheduleStatus = new ScheduleStatus(); timerInfo = new TimerInfo(schedule, scheduleStatus); Assert.Same(scheduleStatus, timerInfo.ScheduleStatus); }
internal static string ToSerializedValue(this ScheduleStatus value) { switch (value) { case ScheduleStatus.Enabled: return("Enabled"); case ScheduleStatus.Disabled: return("Disabled"); } return(null); }
public ActionResult Create() { ViewBag.Title = "Создание расписания"; ViewBag.IsNew = true; var schedule = new Schedule {/*BeginDate = DateTime.Today*/ }; //ViewBag.PatientCode_Data = new SelectList(DataProvider.GetList(new PatientDataFilter()), "Id", "FullName"); ViewBag.TrialCenterID_Data = new SelectList(DataProvider.GetList(new TrialCenterDataFilter()), "Id", "Number"); ViewBag.ScheduleStatuses = ScheduleStatus.GetScheduleStatuses(); return(View(schedule)); }
public async Task Listener_LogsInitialStatus_WhenUsingMonitor() { var status = new ScheduleStatus { Last = new DateTime(2016, 3, 4), Next = new DateTime(2016, 3, 4, 0, 0, 1), LastUpdated = new DateTime(2016, 3, 3, 23, 59, 59) }; var expected = $"Function '{_functionShortName}' initial status: Last='{status.Last.ToString("o")}', Next='{status.Next.ToString("o")}', LastUpdated='{status.LastUpdated.ToString("o")}'"; await RunInitialStatusTestAsync(status, expected); }
public async Task CheckPastDueAsync_ScheduleUpdate_UpdatesStatusFile() { Mock <TimerSchedule> mockSchedule = new Mock <TimerSchedule>(MockBehavior.Strict); DateTime now = DateTime.Now; DateTime next = now + TimeSpan.FromDays(2); ScheduleStatus status = new ScheduleStatus { Last = now, Next = next, LastUpdated = now }; await _monitor.UpdateStatusAsync(_testTimerName, status); mockSchedule.Setup(p => p.GetNextOccurrence(It.IsAny <DateTime>())).Returns(next); TimeSpan pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); // now adjust the schedule DateTime adjustedNext = next - TimeSpan.FromDays(1); mockSchedule.Setup(p => p.GetNextOccurrence(It.IsAny <DateTime>())).Returns(adjustedNext); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); ScheduleStatus updatedStatus = await _monitor.GetStatusAsync(_testTimerName); Assert.Equal(default(DateTime), updatedStatus.Last); Assert.Equal(adjustedNext, updatedStatus.Next); Assert.Equal(now, updatedStatus.LastUpdated); now = now + TimeSpan.FromHours(23); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); now = now + TimeSpan.FromHours(1); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.Zero, pastDueAmount); now = now + TimeSpan.FromHours(1); pastDueAmount = await _monitor.CheckPastDueAsync(_testTimerName, now, mockSchedule.Object, status); Assert.Equal(TimeSpan.FromHours(1), pastDueAmount); }
public async Task StartAsync_SchedulePastDue_InvokesJobFunctionImmediately() { // Set this to true to ensure that the function is only executed once // In this case, because it is run on startup due to being behind schedule, // it shouldn't be run twice. _attribute.RunOnStartup = true; ScheduleStatus status = new ScheduleStatus(); _mockScheduleMonitor.Setup(p => p.GetStatusAsync(_testTimerName)).ReturnsAsync(status); DateTime lastOccurrence = default(DateTime); TimeSpan pastDueAmount = TimeSpan.FromMinutes(3); _mockScheduleMonitor.Setup(p => p.CheckPastDueAsync(_testTimerName, It.IsAny <DateTime>(), It.IsAny <TimerSchedule>(), status)) .Callback <string, DateTime, TimerSchedule, ScheduleStatus>((mockTimerName, mockNow, mockNext, mockStatus) => { lastOccurrence = mockNow; }) .ReturnsAsync(pastDueAmount); _mockScheduleMonitor.Setup(p => p.UpdateStatusAsync(_testTimerName, It.IsAny <ScheduleStatus>())) .Callback <string, ScheduleStatus>((mockTimerName, mockStatus) => { Assert.Equal(lastOccurrence, mockStatus.Last); DateTime expectedNextOccurrence = _schedule.GetNextOccurrence(lastOccurrence); Assert.Equal(expectedNextOccurrence, mockStatus.Next); }) .Returns(Task.FromResult(true)); CancellationToken cancellationToken = CancellationToken.None; await _listener.StartAsync(cancellationToken); TimerInfo timerInfo = (TimerInfo)_triggeredFunctionData.TriggerValue; Assert.Same(status, timerInfo.ScheduleStatus); Assert.True(timerInfo.IsPastDue); _mockTriggerExecutor.Verify(p => p.TryExecuteAsync(It.IsAny <TriggeredFunctionData>(), It.IsAny <CancellationToken>()), Times.Once()); // Make sure we've added the reason for the invocation into the Details Assert.Equal(default(DateTime).ToString("o"), _triggeredFunctionData.TriggerDetails[TimerListener.OriginalScheduleKey]); Assert.Equal("IsPastDue", _triggeredFunctionData.TriggerDetails[TimerListener.UnscheduledInvocationReasonKey]); _listener.Dispose(); }
public async Task RunInitialStatusTestAsync(ScheduleStatus initialStatus, string expected) { _mockScheduleMonitor .Setup(m => m.GetStatusAsync(_testTimerName)) .ReturnsAsync(initialStatus); _mockScheduleMonitor .Setup(m => m.CheckPastDueAsync(_testTimerName, It.IsAny <DateTime>(), _schedule, It.IsAny <ScheduleStatus>())) .ReturnsAsync(TimeSpan.Zero); await _listener.StartAsync(CancellationToken.None); await _listener.StopAsync(CancellationToken.None); _listener.Dispose(); Assert.Equal(expected, _logger.LogMessages.Single(m => m.Level == LogLevel.Debug).FormattedMessage); }
public static void SetScheduleStatus(ScheduleStatus newStatus) { try { //note:locking inside this method is highly misleading //as there is no lock in place between when the caller //decides to call this method and when the lock is acquired //the value could easily change in that time StatusLock.AcquireWriterLock(LockTimeout); try { // It is safe for this thread to read or write // from the shared resource. _status = newStatus; } finally { // Ensure that the lock is released. StatusLock.ReleaseWriterLock(); } } catch (ApplicationException ex) { // The writer lock request timed out. Interlocked.Increment(ref _writerTimeouts); Exceptions.Exceptions.LogException(ex); } }
private void BindStatus() { Status = SchedulingProvider.Instance().GetScheduleStatus(); lblStatus.Text = Status.ToString(); placeCommands.Visible = SchedulingProvider.SchedulerMode == SchedulerMode.TIMER_METHOD; if (Status == ScheduleStatus.STOPPED && SchedulingProvider.SchedulerMode != SchedulerMode.DISABLED) { cmdStart.Enabled = true; cmdStop.Enabled = false; } else if (Status == ScheduleStatus.WAITING_FOR_REQUEST || SchedulingProvider.SchedulerMode == SchedulerMode.DISABLED) { cmdStart.Enabled = false; cmdStop.Enabled = false; } else { cmdStart.Enabled = false; cmdStop.Enabled = true; } }
public static void SetScheduleStatus(ScheduleStatus newStatus) { try { StatusLock.AcquireWriterLock(LockTimeout); try { _status = newStatus; } finally { StatusLock.ReleaseWriterLock(); } } catch (ApplicationException ex) { Interlocked.Increment(ref _writerTimeouts); ErrorLogger.SchedulerProcessException(ex); } }
public CoreScheduler( bool boolDebug, int MaxThreads ) { Status = ScheduleStatus.STOPPED; Debug = boolDebug; if( ! ThreadPoolInitialized ) { InitializeThreadPool( MaxThreads ); } }
internal async Task InvokeJobFunction(DateTime lastOccurrence, bool isPastDue = false) { CancellationToken token = _cancellationTokenSource.Token; TimerInfo timerInfo = new TimerInfo(_attribute.Schedule, _scheduleStatus, isPastDue); TriggeredFunctionData input = new TriggeredFunctionData { TriggerValue = timerInfo }; try { FunctionResult result = await _executor.TryExecuteAsync(input, token); if (!result.Succeeded) { token.ThrowIfCancellationRequested(); } } catch { // We don't want any function errors to stop the execution // schedule. Errors will be logged to Dashboard already. } if (ScheduleMonitor != null) { _scheduleStatus = new ScheduleStatus { Last = lastOccurrence, Next = _schedule.GetNextOccurrence(lastOccurrence) }; await ScheduleMonitor.UpdateStatusAsync(_timerName, _scheduleStatus); } }
public static void SetScheduleStatus( ScheduleStatus objScheduleStatus ) { try { objStatusReadWriteLock.AcquireWriterLock( WriteTimeout ); try { // It is safe for this thread to read or write // from the shared resource. Status = objScheduleStatus; Interlocked.Increment( ref Writes ); } finally { // Ensure that the lock is released. objStatusReadWriteLock.ReleaseWriterLock(); } } catch( ApplicationException ex ) { // The writer lock request timed out. Interlocked.Increment( ref WriterTimeouts ); Exceptions.Exceptions.LogException( ex ); } }
public CoreScheduler( int MaxThreads ) { Status = ScheduleStatus.STOPPED; if( ! ThreadPoolInitialized ) { InitializeThreadPool( MaxThreads ); } }
/// <summary> /// This must run under the lock! /// </summary> private bool? TryGetTaskToRun(ref AbstractTask task) { //this approach gives us an O(1) _removal_ cost from the list LinkedListNode<SchedulePair> node = CurrentThreadQueuedTasks.First; while (node != null) { SchedulePair pair = node.Value; if (pair.Condition()) { task = pair.Task; CurrentThreadQueuedTasks.Remove(node); break; } pair.CannotRunCount += 1; //this is probably a process that is waiting for other processes //move it to the end of the list so we don't have to iterate over it all //the time LinkedListNode<SchedulePair> prev = node; node = node.Next; if (pair.CannotRunCount == 3 && Status != ScheduleStatus.Idle) { pair.CannotRunCount = -1; //give it a bit of a boost for the next time CurrentThreadQueuedTasks.Remove(prev); CurrentThreadQueuedTasks.AddLast(prev); } } if (task == null) // no tasks to run in the curren threa, time to steal some work... { StealWorkFromAnotherThread(); //nothing runs, and there are no tasks that we _can_ run // we are either deadlocked or waiting for an external resource // we will let the idle task decide what to do. if (currentRunningProcessCount == 0) { Status = ScheduleStatus.Idle; task = idleTask; return true; } } return null; }