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.ScheduleAndTimeZone(_logger, _functionLogName, _schedule, 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.InitialStatus(_logger, _functionLogName, ScheduleStatus?.Last.ToString("o"), ScheduleStatus?.Next.ToString("o"), 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.PastDue(_logger, _functionLogName); await InvokeJobFunction(now, isPastDue : true, originalSchedule : ScheduleStatus.Next); } else if (_attribute.RunOnStartup) { // The job is configured to run immediately on startup Logger.RunOnStartup(_logger, _functionLogName); await InvokeJobFunction(now, runOnStartup : true); } // log the next several occurrences to console for visibility string nextOccurrences = TimerInfo.FormatNextOccurrences(_schedule, 5); Logger.NextOccurrences(_logger, _functionLogName, _schedule, nextOccurrences); StartTimer(DateTime.Now); }
public static void OnTimerTick( [TimerTrigger("0 */5 * * * *" /*, handy for debug not great for prod RunOnStartup = true */)] TimerInfo myTimer , ILogger log) { log.LogInformation($"C# Timer trigger function executed at: {DateTime.UtcNow}\n" + $"and next occurances will be at {myTimer.FormatNextOccurrences(5)}"); }
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); }
// ReSharper disable once UnusedMember.Global public async Task PublishStagedReleaseContent([TimerTrigger("%PublishReleaseContentCronSchedule%")] TimerInfo timer, ExecutionContext executionContext, ILogger logger) { logger.LogInformation($"{executionContext.FunctionName} triggered at: {DateTime.Now}"); var scheduled = (await QueryScheduledReleases()).ToList(); if (scheduled.Any()) { var published = new List <ReleaseStatus>(); foreach (var releaseStatus in scheduled) { logger.LogInformation($"Moving content for release: {releaseStatus.ReleaseId}"); await UpdateStage(releaseStatus, Started); try { await _publishingService.PublishStagedReleaseContentAsync(releaseStatus.ReleaseId); published.Add(releaseStatus); } catch (Exception e) { logger.LogError(e, $"Exception occured while executing {executionContext.FunctionName}"); await UpdateStage(releaseStatus, Failed, new ReleaseStatusLogMessage($"Exception in publishing stage: {e.Message}")); } } var releaseIds = published.Select(status => status.ReleaseId).ToArray(); try { if (!PublisherUtils.IsDevelopment()) { await _releaseService.DeletePreviousVersionsStatisticalData(releaseIds); } await _contentService.DeletePreviousVersionsDownloadFiles(releaseIds); await _contentService.DeletePreviousVersionsContent(releaseIds); await _notificationsService.NotifySubscribers(releaseIds); await UpdateStage(published, Complete); } catch (Exception e) { logger.LogError(e, $"Exception occured while executing {executionContext.FunctionName}"); await UpdateStage(published, Failed, new ReleaseStatusLogMessage($"Exception in publishing stage: {e.Message}")); } } logger.LogInformation( $"{executionContext.FunctionName} completed. {timer.FormatNextOccurrences(1)}"); }
// ReSharper disable once UnusedMember.Global public void PublishReleases([TimerTrigger("%PublishReleasesCronSchedule%")] TimerInfo timer, ExecutionContext executionContext, ILogger logger) { logger.LogInformation($"{executionContext.FunctionName} triggered at: {DateTime.Now}"); PublishReleases().Wait(); logger.LogInformation( $"{executionContext.FunctionName} completed. {timer.FormatNextOccurrences(1)}"); }
/// <summary> /// Send, recieve, and process sevis batches using the service. /// </summary> /// <param name="info">The timer trigger instance.</param> /// <returns>The task<./returns> public async Task ProcessTimer([TimerTrigger(typeof(SevisCommSchedule), RunOnStartup = true)] TimerInfo info) { var sevisComm = new SevisComm(this.appSettings, this.ecaHttpMessageHandlerService); await ProcessAsync(this.service, sevisComm, this.appSettings); var nextOccurrenceMessage = info.FormatNextOccurrences(5); logger.Info(nextOccurrenceMessage); }
public void FormatNextOccurrences_ReturnsExpectedString() { DateTime now = new DateTime(2015, 9, 16, 10, 30, 00); TimerInfo timerInfo = new TimerInfo(new CronSchedule("0 0 * * * *"), null); string result = timerInfo.FormatNextOccurrences(10, now); string expected = "The next 10 occurrences of the schedule will be:\r\n" + "9/16/2015 11:00:00 AM\r\n" + "9/16/2015 12:00:00 PM\r\n" + "9/16/2015 1:00:00 PM\r\n" + "9/16/2015 2:00:00 PM\r\n" + "9/16/2015 3:00:00 PM\r\n" + "9/16/2015 4:00:00 PM\r\n" + "9/16/2015 5:00:00 PM\r\n" + "9/16/2015 6:00:00 PM\r\n" + "9/16/2015 7:00:00 PM\r\n" + "9/16/2015 8:00:00 PM\r\n"; Assert.Equal(expected, result); timerInfo = new TimerInfo(new DailySchedule("2:00:00"), null); result = timerInfo.FormatNextOccurrences(5, now); expected = "The next 5 occurrences of the schedule will be:\r\n" + "9/17/2015 2:00:00 AM\r\n" + "9/18/2015 2:00:00 AM\r\n" + "9/19/2015 2:00:00 AM\r\n" + "9/20/2015 2:00:00 AM\r\n" + "9/21/2015 2:00:00 AM\r\n"; Assert.Equal(expected, result); WeeklySchedule weeklySchedule = new WeeklySchedule(); weeklySchedule.Add(DayOfWeek.Monday, new TimeSpan(8, 0, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(9, 30, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(21, 30, 0)); weeklySchedule.Add(DayOfWeek.Friday, new TimeSpan(10, 0, 0)); timerInfo = new TimerInfo(weeklySchedule, null); result = timerInfo.FormatNextOccurrences(5, now); expected = "The next 5 occurrences of the schedule will be:\r\n" + "9/16/2015 9:30:00 PM\r\n" + "9/18/2015 10:00:00 AM\r\n" + "9/21/2015 8:00:00 AM\r\n" + "9/23/2015 9:30:00 AM\r\n" + "9/23/2015 9:30:00 PM\r\n"; Assert.Equal(expected, result); }
// This function will be triggered on a schedule determined by the cron expression provided. In this case it will run every minute. // When this trigger is invoked, it enqueues a Message on the MessageQueue. // To learn more about TimerTriggers, go to https://github.com/Azure/azure-webjobs-sdk-extensions#timertrigger public static void TimedQueueMessage( [TimerTrigger("0 * * * * *", RunOnStartup = true)] TimerInfo timer, [Queue("MessageQueue")] out Message message, TraceWriter log ) { // Create a new message message = new Message() { message = timer.FormatNextOccurrences(1) }; log.Verbose("New message enqueued"); }
public void FormatNextOccurrences_ReturnsExpectedString() { DateTime now = new DateTime(2015, 9, 16, 10, 30, 00); TimerInfo timerInfo = new TimerInfo(new CronSchedule(CrontabSchedule.Parse("0 * * * *")), null); string result = timerInfo.FormatNextOccurrences(10, now); var expectedDates = Enumerable.Range(11, 10) .Select(hour => new DateTime(2015, 09, 16, hour, 00, 00)) .Select(dateTime => string.Format("{0}\r\n", dateTime)) .ToArray(); string expected = "The next 10 occurrences of the schedule will be:\r\n" + string.Join(string.Empty, expectedDates); Assert.Equal(expected, result); timerInfo = new TimerInfo(new DailySchedule("2:00:00"), null); result = timerInfo.FormatNextOccurrences(5, now); expectedDates = Enumerable.Range(17, 5) .Select(day => new DateTime(2015, 09, day, 02, 00, 00)) .Select(dateTime => string.Format("{0}\r\n", dateTime)) .ToArray(); expected = "The next 5 occurrences of the schedule will be:\r\n" + string.Join(string.Empty, expectedDates); Assert.Equal(expected, result); WeeklySchedule weeklySchedule = new WeeklySchedule(); weeklySchedule.Add(DayOfWeek.Monday, new TimeSpan(8, 0, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(9, 30, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(21, 30, 0)); weeklySchedule.Add(DayOfWeek.Friday, new TimeSpan(10, 0, 0)); timerInfo = new TimerInfo(weeklySchedule, null); result = timerInfo.FormatNextOccurrences(5, now); expected = "The next 5 occurrences of the schedule will be:\r\n" + new DateTime(2015, 09, 16, 21, 30, 00).ToString() + "\r\n" + new DateTime(2015, 09, 18, 10, 00, 00).ToString() + "\r\n" + new DateTime(2015, 09, 21, 08, 00, 00).ToString() + "\r\n" + new DateTime(2015, 09, 23, 09, 30, 00).ToString() + "\r\n" + new DateTime(2015, 09, 23, 21, 30, 00).ToString() + "\r\n"; Assert.Equal(expected, result); }
// ReSharper disable once UnusedMember.Global public async Task PublishReleases([TimerTrigger("%PublishReleasesCronSchedule%")] TimerInfo timer, ExecutionContext executionContext, ILogger logger) { logger.LogInformation("{0} triggered at: {1}", executionContext.FunctionName, DateTime.UtcNow); await PublishReleases(); logger.LogInformation( "{0} completed. {1}", executionContext.FunctionName, timer.FormatNextOccurrences(1)); }
public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); if (_timer != null && _timer.Enabled) { throw new InvalidOperationException("The listener has already been started."); } // See if we need to invoke the function immediately now, and if // so invoke it. DateTime now = DateTime.Now; if (_attribute.UseMonitor && await _scheduleMonitor.IsPastDueAsync(_timerName, now, _schedule)) { // If schedule monitoring is enabled for this timer job, check to see if we've // missed an occurrence since we last started. If we have, invoke it immediately. _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 async Task Run( [TimerTrigger("%SqlResourceDatabaseSyncSchedule%")] TimerInfo timerInfo, [Table("InventorySqlResourceDatabases", Connection = "StorageConnectionAppSetting")] CloudTable sqlResourceDatabaseTable, ILogger log) { log.LogInformation($"C# Sql Resource Database Sync Timer trigger function executed at: {DateTime.UtcNow}. Next occurrence: {timerInfo.FormatNextOccurrences(1)}"); DataTable stageDataTable = new DataTable { Columns = { { "ID", typeof(int) }, { "ServerNameId", typeof(string) }, { "Name", typeof(string) }, { "ServerName", typeof(string) }, { "SubscriptionId", typeof(string) }, { "CreatedOn", typeof(DateTime) }, { "LastSeenOn", typeof(DateTime) } } }; stageDataTable.PrimaryKey = new[] { stageDataTable.Columns[0] }; TableContinuationToken token = null; do { TableQuerySegment <SqlServerDatabaseEntity> queryResult = await sqlResourceDatabaseTable.ExecuteQuerySegmentedAsync(new TableQuery <SqlServerDatabaseEntity>(), token); int index = 1; foreach (SqlServerDatabaseEntity result in queryResult) { DataRow row = stageDataTable.NewRow(); row["ID"] = index; row["ServerNameId"] = result.ServerNameId; row["Name"] = result.RowKey; row["ServerName"] = result.PartitionKey; row["SubscriptionId"] = result.SubscriptionId; row["CreatedOn"] = result.CreatedOn; row["LastSeenOn"] = result.LastSeenOn; stageDataTable.Rows.Add(row); index++; } token = queryResult.ContinuationToken; } while (token != null); await _sqlResourceDatabaseSyncService.SyncSqlResourceDatabasesToAzSQLAsync(stageDataTable); }
public async Task Run( [TimerTrigger("%SubscriptionSyncSchedule%")] TimerInfo timerInfo, [Table("InventorySubscription", Connection = "StorageConnectionAppSetting")] CloudTable inventorySubscription, ILogger log) { log.LogInformation($"C# Timer trigger function executed at: {DateTime.UtcNow}. Next occurrence: {timerInfo.FormatNextOccurrences(1)}"); DataTable stageDataTable = new DataTable { Columns = { { "ID", typeof(Guid) }, { "Name", typeof(string) }, { "CreatedOn", typeof(DateTime) }, { "LastSeenOn", typeof(DateTime) } } }; stageDataTable.PrimaryKey = new[] { stageDataTable.Columns[0] }; TableContinuationToken token = null; do { TableQuerySegment <SubscriptionEntity> queryResult = await inventorySubscription.ExecuteQuerySegmentedAsync(new TableQuery <SubscriptionEntity>(), token); foreach (SubscriptionEntity result in queryResult) { if (Guid.TryParse(result.PartitionKey, out Guid subscriptionId)) { log.LogInformation($"Converted Subscription Id {result.PartitionKey} to a Guid"); } else { log.LogWarning($"Unable to convert Subscription Id {result.PartitionKey} to a Guid. Skipping the entity."); break; } DataRow row = stageDataTable.NewRow(); row["ID"] = subscriptionId; row["Name"] = result.SubscriptionName; row["CreatedOn"] = result.CreatedOn; row["LastSeenOn"] = result.LastSeenOn; stageDataTable.Rows.Add(row); } token = queryResult.ContinuationToken; } while (token != null); await _subscriptionSyncService.SyncSubscriptionsToAzSQLAsync(stageDataTable); }
public static void DoStuff([TimerTrigger("*/10 * * * * *", RunOnStartup = true)] TimerInfo myTimer, ILogger log) { log.LogInformation($"I was triggered at {DateTime.Now.ToLongTimeString()}."); log.LogInformation(myTimer.FormatNextOccurrences(1)); }
public void FormatNextOccurrences_ReturnsExpectedString() { // There's no way to mock the OS TimeZoneInfo, so let's make sure this // works on both UTC and non-UTC string DateFormatter(DateTime d) { if (TimeZoneInfo.Local == TimeZoneInfo.Utc) { return(d.ToString(TimerInfo.DateTimeFormat)); } return($"{d.ToString(TimerInfo.DateTimeFormat)} ({d.ToUniversalTime().ToString(TimerInfo.DateTimeFormat)})"); } DateTime now = new DateTime(2015, 9, 16, 10, 30, 00, DateTimeKind.Local); CronSchedule cronSchedule = new CronSchedule(CrontabSchedule.Parse("0 * * * *")); string result = TimerInfo.FormatNextOccurrences(cronSchedule, 10, now: now); var expectedDates = Enumerable.Range(11, 10) .Select(hour => new DateTime(2015, 09, 16, hour, 00, 00, DateTimeKind.Local)) .Select(dateTime => $"{DateFormatter(dateTime)}\r\n") .ToArray(); string expected = $"The next 10 occurrences of the schedule ({cronSchedule}) will be:\r\n" + string.Join(string.Empty, expectedDates); Assert.Equal(expected, result); // Test the internal method with timer name specified string timerName = "TestTimer"; TimerSchedule schedule = new DailySchedule("2:00:00"); result = TimerInfo.FormatNextOccurrences(schedule, 5, now, timerName); expectedDates = Enumerable.Range(17, 5) .Select(day => new DateTime(2015, 09, day, 02, 00, 00, DateTimeKind.Local)) .Select(dateTime => $"{DateFormatter(dateTime)}\r\n") .ToArray(); expected = $"The next 5 occurrences of the 'TestTimer' schedule ({schedule}) will be:\r\n" + string.Join(string.Empty, expectedDates); Assert.Equal(expected, result); WeeklySchedule weeklySchedule = new WeeklySchedule(); weeklySchedule.Add(DayOfWeek.Monday, new TimeSpan(8, 0, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(9, 30, 0)); weeklySchedule.Add(DayOfWeek.Wednesday, new TimeSpan(21, 30, 0)); weeklySchedule.Add(DayOfWeek.Friday, new TimeSpan(10, 0, 0)); schedule = weeklySchedule; result = TimerInfo.FormatNextOccurrences(schedule, 5, now, timerName); expected = $"The next 5 occurrences of the 'TestTimer' schedule ({weeklySchedule}) will be:\r\n" + DateFormatter(new DateTime(2015, 09, 16, 21, 30, 00, DateTimeKind.Local)) + "\r\n" + DateFormatter(new DateTime(2015, 09, 18, 10, 00, 00, DateTimeKind.Local)) + "\r\n" + DateFormatter(new DateTime(2015, 09, 21, 08, 00, 00, DateTimeKind.Local)) + "\r\n" + DateFormatter(new DateTime(2015, 09, 23, 09, 30, 00, DateTimeKind.Local)) + "\r\n" + DateFormatter(new DateTime(2015, 09, 23, 21, 30, 00, DateTimeKind.Local)) + "\r\n"; Assert.Equal(expected, result); }
public async Task PublishStagedReleaseContent([TimerTrigger("%PublishReleaseContentCronSchedule%")] TimerInfo timer, ExecutionContext executionContext, ILogger logger) { logger.LogInformation("{0} triggered at: {1}", executionContext.FunctionName, DateTime.UtcNow); var scheduled = (await QueryScheduledReleases()).ToList(); if (scheduled.Any()) { var published = new List <ReleasePublishingStatus>(); foreach (var releaseStatus in scheduled) { logger.LogInformation("Moving content for release: {0}", releaseStatus.ReleaseId); await UpdateStage(releaseStatus, Started); try { await _publishingService.PublishStagedReleaseContent(releaseStatus.ReleaseId, releaseStatus.PublicationSlug); published.Add(releaseStatus); } catch (Exception e) { logger.LogError(e, "Exception occured while executing {0}", executionContext.FunctionName); await UpdateStage(releaseStatus, Failed, new ReleasePublishingStatusLogMessage($"Exception in publishing stage: {e.Message}")); } } var releaseIds = published.Select(status => status.ReleaseId).ToArray(); try { if (!EnvironmentUtils.IsLocalEnvironment()) { await _releaseService.DeletePreviousVersionsStatisticalData(releaseIds); } // Invalidate the cached trees in case any methodologies/publications // are now accessible for the first time after publishing these releases await _contentService.DeleteCachedTaxonomyBlobs(); await _contentService.DeletePreviousVersionsDownloadFiles(releaseIds); await _contentService.DeletePreviousVersionsContent(releaseIds); await _notificationsService.NotifySubscribersIfApplicable(releaseIds); await UpdateStage(published, Complete); } catch (Exception e) { logger.LogError(e, "Exception occured while executing {0}", executionContext.FunctionName); await UpdateStage(published, Failed, new ReleasePublishingStatusLogMessage($"Exception in publishing stage: {e.Message}")); } } logger.LogInformation( "{0} completed. {1}", executionContext.FunctionName, timer.FormatNextOccurrences(1)); }
public async Task RunAsync([TimerTrigger("%TimerInterval%")] TimerInfo timer, ILogger logger) { timer = timer ?? throw new ArgumentNullException(nameof(timer)); logger.LogInformation("SendReminderFunction function started."); await this.SendPendingRequestsRemindersAsync(logger); await this.SendFillTimesheetRemindersAsync(logger); logger.LogInformation("SendReminderFunction function finished, next occurrence at: " + timer.FormatNextOccurrences(1)); }
public async Task Run( [TimerTrigger("%SubscriptionInventorySchedule%")] TimerInfo timerInfo, [Table("InventorySubscription", Connection = "StorageConnectionAppSetting")] CloudTable inventorySubscription, [Queue("outqueue", Connection = "StorageConnectionAppSetting")] ICollector <string> msg, ILogger log) { log.LogInformation($"C# Timer trigger function executed at: {DateTime.UtcNow}. Next occurrence: {timerInfo.FormatNextOccurrences(1)}"); IReadOnlyList <SubscriptionDto> subscriptions = await _subscriptionInventoryService.GetSubscriptionsAsync(); foreach (SubscriptionDto sub in subscriptions) { await inventorySubscription.CreateIfNotExistsAsync(); DateTime init = DateTime.Now; SubscriptionEntity subEntity = new SubscriptionEntity() { PartitionKey = sub.SubscriptionId, RowKey = Constants.SubscriptionEntitySummaryRowKey, SubscriptionName = sub.SubscriptionName, CreatedOn = init, LastSeenOn = init }; TableOperation retrieveOperation = TableOperation.Retrieve <SubscriptionEntity>(sub.SubscriptionId, Constants.SubscriptionEntitySummaryRowKey); TableResult retrievedEntity = await inventorySubscription.ExecuteAsync(retrieveOperation); if (retrievedEntity.Result == null) { TableOperation insertOperation = TableOperation.Insert(subEntity); await inventorySubscription.ExecuteAsync(insertOperation); } else { subEntity.CreatedOn = ((SubscriptionEntity)retrievedEntity.Result).CreatedOn; TableOperation mergeOperation = TableOperation.InsertOrMerge(subEntity); await inventorySubscription.ExecuteAsync(mergeOperation); } msg.Add(sub.SubscriptionId); } }
public string FormatNextOccurrences(int count, DateTime?now = null) { return(_timer.FormatNextOccurrences(count, now)); }