public async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { var input = context.GetInput <NotifySupportOrchestratorInput>(); if (input.SupportContacts == null) { // Let's get the support contacts from storage. var supportContacts = await context.CallActivityAsync <IEnumerable <SupportContactEntity> >( nameof(GetSupportContactActivity), "A"); input.SupportContacts = supportContacts.ToArray(); } var notificationOrchestratorInput = SendNotificationOrchestratorInputBuilder.Build(input); var notificationResult = await context.CallSubOrchestratorAsync <SendNotificationOrchestratorResult>( nameof(SendNotificationOrchestrator), notificationOrchestratorInput); if (!notificationResult.CallbackReceived && notificationOrchestratorInput.SupportContact != input.SupportContacts.Last()) { // Calls have not been answered, let's try the next contact. input.SupportContactIndex++; logger.LogInformation($"=== Next Contact={input.SupportContacts[input.SupportContactIndex].PhoneNumber} ==="); context.ContinueAsNew(input); } else { logger.LogInformation($"=== Completed {nameof(NotifySupportOrchestrator)} for {notificationResult.PhoneNumber} with callback received={notificationResult.CallbackReceived} on attempt={notificationResult.Attempt}. ==="); } }
private static async Task <ICommandResult> SwitchCommandAsync(IDurableOrchestrationContext orchestrationContext, Input functionContext, ICommandResult commandResult, ILogger log) { try { await orchestrationContext .AuditAsync(functionContext.Command, commandResult, functionContext.Provider) .ConfigureAwait(true); orchestrationContext.SetCustomStatus($"Switching command", log); var project = await orchestrationContext .GetProjectAsync(functionContext.Command.ProjectId, allowUnsafe : true) .ConfigureAwait(true); functionContext.Command = new ProviderProjectUpdateCommand(functionContext.Command.User as User, project.PopulateExternalModel(), functionContext.Command.CommandId); orchestrationContext.ContinueAsNew(functionContext); } catch (Exception exc) { orchestrationContext.SetCustomStatus($"Switching command failed: {exc.Message}", log, exc); commandResult ??= functionContext.Command.CreateResult(); commandResult.Errors.Add(exc); } return(commandResult); }
public async Task <object> Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var input = context.GetInput <Input>(); try { var output = await context.CallActivityAsync <object>(input.ActivityFunctionName, input.ActivityFunctionInput); await this.circuitBreakerClient.RecordSuccess(input.CircuitBreakerOptions.CircuitBreakerId, log, context); return(output); } catch (Exception exception) { await this.circuitBreakerClient.RecordFailure(input.CircuitBreakerOptions.CircuitBreakerId, log, context); var handled = await HandleException(exception, context); if (handled) { context.ContinueAsNew(input); return(null); } else { throw; } } }
public static async Task <int> Counter([OrchestrationTrigger] IDurableOrchestrationContext ctx) { int currentValue = ctx.GetInput <int>(); string operation = await ctx.WaitForExternalEvent <string>("operation"); bool done = false; switch (operation?.ToLowerInvariant()) { case "incr": currentValue++; break; case "decr": currentValue--; break; case "end": done = true; break; } // Allow clients to track the current value. ctx.SetCustomStatus(currentValue); if (!done) { ctx.ContinueAsNew(currentValue, preserveUnprocessedEvents: true); } return(currentValue); }
public async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { try { //var parallelTasks = new List<Task<int>>(); // Get a list of N callLog items to process in parallel. var workBatch = await context.CallActivityAsync <IList <CallLog> >("PostCallCleanupEternalOrchestrator_GetCallLog", null); log.LogInformation($"PostCallCleanupEternalOrchestrator: WorkBatch items to process: {JsonConvert.SerializeObject(workBatch)}"); var tasks = new Task <CallLog> [workBatch.Count]; for (int i = 0; i < workBatch.Count; i++) { tasks[i] = context.CallActivityAsync <CallLog>("PostCallCleanupEternalOrchestrator_PostCallCleanup", workBatch[i]); } } catch (Exception ex) { log.LogWarning(ex, $"PostCallCleanupEternalOrchestrator: An error occurred while processing orchestrator. Details: {ex.Message}"); } finally { // sleep for n minutes before running again int minuteInterval = int.Parse(_configuration["PostCallCleanupCheckMinuteInterval"] ?? "5"); DateTime nextCleanup = context.CurrentUtcDateTime.AddMinutes(minuteInterval); await context.CreateTimer(nextCleanup, CancellationToken.None); log.LogInformation("Restarting orchestrator"); context.ContinueAsNew(null); } }
public async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log = context.CreateReplaySafeLogger(log); // Real life scenario would be to create a new report every day at midnight // DateTime endTime = context.CurrentUtcDateTime.AddDays(1).Date; DateTime reportCreationTime = context.CurrentUtcDateTime.AddSeconds(15); var reportCreationTimer = context.CreateTimer(reportCreationTime, CancellationToken.None); var reportingCancelationRequest = context.WaitForExternalEvent("CancelReportingTask"); var completedTask = await Task.WhenAny(reportCreationTimer, reportingCancelationRequest); if (completedTask == reportCreationTimer) { await context.CallActivityAsync(nameof(ReportCreationActivity), null); log.LogInformation("Starting a new instance cycle"); // Eternal orchestration until canceled context.ContinueAsNew(null); } else { log.LogInformation("Reporting task cancelation was requested"); } }
public async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { await context.CreateTimer( context.CurrentUtcDateTime.AddHours(1), CancellationToken.None); context.ContinueAsNew(null); }
public static async Task CallOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext ctx) { var startArgs = await ctx.WaitForExternalEvent <StartOrchestrationArgs>("StartOrchestration"); await ctx.CallSubOrchestratorAsync <object>( startArgs.FunctionName, startArgs.InstanceId, startArgs.Input); ctx.ContinueAsNew(null, preserveUnprocessedEvents: true); }
public static async Task <bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { var hostId = context.GetInput <EntityId>(); logger = context.CreateReplaySafeLogger(logger); var state = await context.CallActivityAsync <EntityStateResponse <HostEntity> >(nameof(GetHostState), hostId); if (!state.EntityExists) { logger.LogError("Attempted to start watch dog for a non-existing host entity: {hostId}", hostId); throw new Exception("Failed to start watch dog for non-existing entity"); } if (context.InstanceId != HostEntity.calculateHash(state.EntityState.IpAddress)) { throw new Exception("violent suicide committed on watchdog!"); } if (state.EntityState.ipv4Support || state.EntityState.ipv6Support) { context.SignalEntity(HostList.Id, HostList.AddHost, hostId); logger.LogInformation("Adding {hostId} to active hosts", hostId); } else { context.SignalEntity(HostList.Id, HostList.RemoveHost, hostId); logger.LogInformation("Removing {hostId} from active hosts", hostId); } var nextCheck = state.EntityState.DnsUptime switch { { } v when v < 0.2m => TimeSpan.FromDays(1), { } v when v >= 0.2m && v < 0.75m => TimeSpan.FromHours(12 + new Random().Next(-1, 1)), { } v when v >= 0.75m => TimeSpan.FromMinutes(60 + new Random().Next(-5, 5)), _ => TimeSpan.FromHours(1) }; #if DEBUG nextCheck = TimeSpan.FromMinutes(5); #endif await context.CreateTimer(context.CurrentUtcDateTime + nextCheck, CancellationToken.None); var host = context.CreateEntityProxy <IHostEntity>(hostId); await host.CheckUp(); context.ContinueAsNew(hostId); return(true); } }
public static async Task ContinueAsNew(this IDurableOrchestrationContext orchestration, object input, TimeSpan delay, bool preserveUnprocessedEvents = false) { if (orchestration is null) { throw new ArgumentNullException(nameof(orchestration)); } await orchestration .CreateTimer(delay) .ConfigureAwait(true); orchestration.ContinueAsNew(input, preserveUnprocessedEvents); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { logger = context.CreateReplaySafeLogger(logger); var expiration = context.GetInput <DateTime>(); var minDelay = TimeSpan.FromSeconds(10); var maxDelay = TimeSpan.FromSeconds(30); var delayDelta = expiration - context.CurrentUtcDateTime; var waitTime = TimeSpan.FromSeconds(Math.Clamp(delayDelta.TotalSeconds, minDelay.TotalSeconds, maxDelay.TotalSeconds)); var wakeupTime = context.CurrentUtcDateTime + waitTime; logger.LogInformation($">>>>>> [ORC] Orchestration begins with expiration at { expiration }, waitTime { waitTime }, next wakeup at { wakeupTime }"); using (var cts = new CancellationTokenSource()) { var timerTask = context.CreateTimer(wakeupTime, cts.Token); var dataTask = context.WaitForExternalEvent <string>(DataEventName); var winner = await Task.WhenAny(dataTask, timerTask); if (winner == dataTask) { cts.Cancel(); var data = await dataTask; logger.LogInformation($">>>>>> [ORC] Data Event Triggered, data = { data }"); var result = await context.CallActivityAsync <string>(nameof(SayHello), data); logger.LogInformation($">>>>>> [ORC] Activity result { result }"); } else { logger.LogInformation(">>>>>> [ORC] Timer Triggered"); if (context.CurrentUtcDateTime >= expiration) { logger.LogInformation(">>>>>> [ORC] Orchestration expired"); return; } } logger.LogInformation(">>>>>> [ORC] Restarting for another round"); context.ContinueAsNew(expiration, true); } }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var message = JObject.Parse(context.GetInput <string>()); var maxRetryNumber = (int)message[MessagePropertyNames.MaxRetryNumber]; var renewalIntervalInSeconds = (int)message[MessagePropertyNames.RenewalIntervalInSeconds]; var contextInstanceId = context.InstanceId; log.LogInformation($"ID '{contextInstanceId}': Iteration {message[MessagePropertyNames.RetryCount]}/{maxRetryNumber} started."); DateTime deadline = context.CurrentUtcDateTime.Add(TimeSpan.FromSeconds(renewalIntervalInSeconds)); using (var cts = new CancellationTokenSource()) { Task terminationTask = context.WaitForExternalEvent(TerminationEventName); Task timeoutTask = context.CreateTimer(deadline, cts.Token); Task winner = await Task.WhenAny(terminationTask, timeoutTask); if (winner == terminationTask) { // success case log.LogInformation($"ID '{contextInstanceId}': Lock-handling stopped by using instance."); cts.Cancel(); } else if ((int)message[MessagePropertyNames.RetryCount] < maxRetryNumber) { // timeout case log.LogInformation($"ID '{contextInstanceId}': Interval reached for lock-renewal."); (bool terminated, string nextMessageJson) = await context.CallActivityAsync <(bool, string)>(nameof(RenewLockActivity), (contextInstanceId, message)); if (!terminated) { log.LogInformation($"ID '{contextInstanceId}': Scheduling next iteration."); context.ContinueAsNew(nextMessageJson); } } else { log.LogInformation($"ID '{contextInstanceId}': Maximal number of cycles reached. Aborting lock-handling."); } } }
public async Task Periodic_Cleanup_Loop( [OrchestrationTrigger] IDurableOrchestrationContext context) { var expire = context.GetInput <Expire>(); await context.CallActivityAsync(nameof(this.CleanUpNotification), "CleanUp!"); DateTime nextCleanup = context.CurrentUtcDateTime.AddSeconds(10); if (expire.Value > nextCleanup) { await context.CreateTimer(nextCleanup, CancellationToken.None); context.ContinueAsNew(expire); } }
public static async Task Orchestration( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var args = context.GetInput <HttpTriggerArgs>(); using (var cts = new CancellationTokenSource()) { await context.WaitForExternalEvent("WakeUp", TimeSpan.FromSeconds(30)); var entityId = new EntityId(nameof(DeviceEntity), args.DeviceId); var onlineStatusTask = await context.CallEntityAsync <string>(entityId, nameof(DeviceEntity.Getonline)); } context.ContinueAsNew(args, false); }
public static async Task <string> ContinueAsNew_Repro285([OrchestrationTrigger] IDurableOrchestrationContext ctx) { var input = ctx.GetInput <int>(); if (input == 0) { ctx.ContinueAsNew(1); return("continue as new"); } else if (input == 1) { await ctx.CreateTimer <object>(ctx.CurrentUtcDateTime + TimeSpan.FromSeconds(1), null, CancellationToken.None); } return("ok"); }
public static async Task WaitingOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log) { var orchestratorArgs = ctx.GetInput <OrchestratorArgs>(); // Using an entity as a cache for the data of the device like // - OfflineAfter, static metadata, typically coming from a device registry (loaded during initialization of entity) // - LastMessageReceived, 'hot' data, not essential for current PoC but could be usefull later on var entity = new EntityId(nameof(DeviceEntity), orchestratorArgs.DeviceId); var offlineAfter = await ctx.CallEntityAsync <TimeSpan>(entity, "GetOfflineAfter"); var lastActivity = await ctx.CallEntityAsync <DateTime?>(entity, "GetLastMessageReceived"); if (!ctx.IsReplaying && (!lastActivity.HasValue || ctx.CurrentUtcDateTime - lastActivity > offlineAfter)) { // This runs for the first message ever for a device id // Also, after a device has gone offline and comes back online, this orchestrator starts again Last Activity should then be longer ago then the offline after log.LogInformation($"Device {orchestratorArgs.DeviceId}, was unkown or offline and is now online!"); await ctx.CallActivityAsync(nameof(SendStatusUpdate), new StatusUpdateArgs(orchestratorArgs.DeviceId, true)); } try { await ctx.WaitForExternalEvent("MessageReceived", offlineAfter); if (!ctx.IsReplaying) { log.LogInformation($"Message received for device {orchestratorArgs.DeviceId}, resetting timeout of {offlineAfter.TotalSeconds} seconds offline detection..."); } ctx.ContinueAsNew(orchestratorArgs); return; } catch (TimeoutException) { if (!ctx.IsReplaying) { log.LogWarning($"Device {orchestratorArgs.DeviceId}, is now offline after waiting for {offlineAfter.TotalSeconds} seconds"); } await ctx.CallActivityAsync(nameof(SendStatusUpdate), new StatusUpdateArgs(orchestratorArgs.DeviceId, false)); return; } }
public static async Task <int> PeriodicTask( [OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log) { log = ctx.CreateReplaySafeLogger(log); var timesRun = ctx.GetInput <int>(); timesRun++; log.LogInformation($"Starting the PeriodicTask activity {ctx.InstanceId}, {timesRun}"); await ctx.CallActivityAsync("A_PeriodicActivity", timesRun); var nextRun = ctx.CurrentUtcDateTime.AddSeconds(30); await ctx.CreateTimer(nextRun, CancellationToken.None); ctx.ContinueAsNew(timesRun); return(timesRun); }
public static async Task IotHubScaleOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log.LogInformation("IotHubScaleOrchestrator started"); // launch and wait on the "worker" function await context.CallActivityAsync(nameof(IotHubScaleWorker), null); // register a timer with the durable functions infrastructure to re-launch the orchestrator in the future DateTime wakeupTime = context.CurrentUtcDateTime.Add(TimeSpan.FromMinutes(JobFrequencyMinutes)); await context.CreateTimer(wakeupTime, CancellationToken.None); log.Info(String.Format("IotHubScaleOrchestrator done... tee'ing up next instance in {0} minutes.", JobFrequencyMinutes.ToString())); // end this 'instance' of the orchestrator and schedule another one to start based on the timer above context.ContinueAsNew(null); }
public async Task <SendNotificationOrchestratorResult> Run( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { var input = context.GetInput <SendNotificationOrchestratorInput>(); // Use Durable Entity to store orchestrator instanceId based on phonenumber var entityId = new EntityId(nameof(NotificationOrchestratorInstanceEntity), input.SupportContact.PhoneNumber); context.SignalEntity( entityId, nameof(NotificationOrchestratorInstanceEntity.Set), context.InstanceId); var activityInput = new SendNotificationActivityInput { Attempt = input.NotificationAttemptCount, Message = input.Message, PhoneNumber = input.SupportContact.PhoneNumber }; await context.CallActivityAsync( nameof(SendNotificationActivity), activityInput); var waitTimeBetweenRetry = TimeSpan.FromSeconds(input.WaitTimeForEscalationInSeconds / input.MaxNotificationAttempts); // Orchestrator will wait until the event is received or waitTimeBetweenRetry is passed, defaults to false. var callBackResult = await context.WaitForExternalEvent <bool>(EventNames.Callback, waitTimeBetweenRetry, false); if (!callBackResult && input.NotificationAttemptCount < input.MaxNotificationAttempts) { // Call has not been answered, let's try again! input.NotificationAttemptCount++; context.ContinueAsNew(input); } // Call has been answered or MaxNotificationAttempts has been reached. var result = new SendNotificationOrchestratorResult { Attempt = input.NotificationAttemptCount, PhoneNumber = input.SupportContact.PhoneNumber, CallbackReceived = callBackResult }; return(result); }
public static async Task PeriodicCrawlExecuter( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger) { logger = context.CreateReplaySafeLogger(logger); var dueTime = context.CurrentUtcDateTime.AddMinutes(15); dueTime = dueTime.AddSeconds(-dueTime.Second); var needCrawlContests = context.GetInput <List <string> >() ?? new List <string>(); var tasks = new Task <bool> [needCrawlContests.Count]; logger.LogInformation($"needCrawlContests : [{string.Join(", ", needCrawlContests)}]"); for (int i = 0; i < tasks.Length; i++) { logger.LogInformation($"start updating {needCrawlContests[i]}"); tasks[i] = context.CallActivityAsync <bool>("UpdateAPerfs", needCrawlContests[i]); } for (int i = 0; i < tasks.Length; i++) { await tasks[i]; } logger.LogInformation("completed"); for (int i = tasks.Length - 1; i >= 0; i--) { if (!tasks[i].Result) { needCrawlContests.RemoveAt(i); } } if (needCrawlContests.Count == 0) { return; } await context.CreateTimer(dueTime, CancellationToken.None); context.ContinueAsNew(needCrawlContests); }
public static async Task RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { await context.CallActivityAsync("EternalOrchestration_SayHello", "Sergio"); var fireAt = context.CurrentUtcDateTime.AddSeconds(5); await context.CreateTimer(fireAt, CancellationToken.None); context.ContinueAsNew(null); //if ((context.CurrentUtcDateTime - _beginAt).TotalSeconds < 60) //{ // context.ContinueAsNew(null); //} //else //{ // context.SetOutput("The end."); //} }
public static async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { log.LogInformation($"Started orchestrator with ID {context.InstanceId}"); var retryPolicy = new RetryOptions(new TimeSpan(0, 0, 0, 1), 10); retryPolicy.BackoffCoefficient = 2; var learners = await context.CallActivityAsync <List <Learner> >("GetLearners", null); if (learners.Count > 0) { log.LogInformation($"{learners.Count} learners to be processed"); var learnerTasks = new List <Task>(); foreach (var learner in learners) { log.LogInformation($"Creating request to calculate payments for ULN: {learner.Uln} - LegalEntity {learner.LegalEntityId}"); Task task = context.CallActivityWithRetryAsync("CalculatePaymentsForLearner", retryPolicy, learner); learnerTasks.Add(task); } log.LogInformation($"{learnerTasks.Count} tasks created"); await Task.WhenAll(learnerTasks); context.ContinueAsNew(null); return; } log.LogInformation("All payment calculations complete"); var parallelTasks = new List <Task>(); var legalEntities = await context.CallActivityAsync <List <long> >("GetLegalEntities", null); foreach (var legalEntity in legalEntities) { Task task = context.CallActivityWithRetryAsync("PayLegalEntity", retryPolicy, legalEntity); parallelTasks.Add(task); } await Task.WhenAll(parallelTasks); }
public static async Task <int> Orchestrator_EternalFunc( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { Counter counter = context.GetInput <Counter>(); await context.CallActivityAsync(nameof(Activity_EternalFunc), counter.Value); DateTime timeoutAt = context.CurrentUtcDateTime.AddSeconds(counter.Value * 5); await context.CreateTimer(timeoutAt, CancellationToken.None); if (++counter.Value < 5) { context.ContinueAsNew(counter); } else { log.LogInformation("EternalFunc is stopped."); } return(counter.Value); }
public static async Task StartTimeoutAsync([OrchestrationTrigger] IDurableOrchestrationContext ctx) { var entityId = ctx.InstanceId; using (var timeout = new CancellationTokenSource()) { DateTime dueTime = ctx.CurrentUtcDateTime.AddMinutes(30); Task durableTimeout = ctx.CreateTimer(dueTime, timeout.Token); Task cancelEvent = ctx.WaitForExternalEvent("TimeoutReset"); if (cancelEvent == await Task.WhenAny(cancelEvent, durableTimeout)) { timeout.Cancel(); ctx.ContinueAsNew(entityId); } else { var proxy = ctx.CreateEntityProxy <ICartActions>(entityId); proxy.Delete(); } } }
public async Task EnqAnswerOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { // アンケート回答をまとめるリスト(再帰呼び出し時は引数から受け取る) var list = context.GetInput <List <string> >() ?? new List <string>(); // 「answer」という名前のイベントが起きるまで待機 // 発生後、その結果を value に受け取る var value = await context.WaitForExternalEvent <(int index, string message, string replyToken)>("answer"); log.LogInformation($"EnqAnswerOrchestrator - index: {value.index}"); // リストに回答を追加 list.Add(value.message); // インデックスが -1 なら終了とみなす if (value.index == -1) { // 完成したリストをリプライトークンとともに確認返信アクティビティに渡す await context.CallActivityAsync(nameof(SendSummaryActivity), (value.replyToken, list)); } else { // 回答インデックスを更新 context.SetCustomStatus(value.index); // 質問返信アクティビティ呼び出し // あえてオーケストレーターの最後で遠回しに返信処理を呼ぶのは、 // インデックス更新前に返信されて同じ質問が返るのを防ぐため。 // 返信などもアクティビティに任せないとおかしくなる(オーケストレーターは冪等性を維持) await context.CallActivityAsync(nameof(SendQuestionActivity), (value.replyToken, value.index + 1)); // オーケストレーターを再帰的に呼び出す context.ContinueAsNew(list); } }
public static async Task <string> ContinueAsNewMultipleTimersAndEvents([OrchestrationTrigger] IDurableOrchestrationContext ctx) { var input = ctx.GetInput <int>(); if (input > 0) { var cts = new CancellationTokenSource(); var timerTask = ctx.CreateTimer <object>(ctx.CurrentUtcDateTime + TimeSpan.FromDays(1), null, cts.Token); var eventTask = ctx.WaitForExternalEvent($"signal{input}"); await Task.WhenAny(timerTask, eventTask); cts.Cancel(); ctx.ContinueAsNew(input - 1); return(input.ToString()); } else if (input == 0) { return("ok"); } else { return("error"); } }
public static async Task ProjectCommandMonitoringOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var notification = context.GetInput <ProjectCommandNotification>(); await context .CreateTimer(context.CurrentUtcDateTime.AddSeconds(5), CancellationToken.None) .ConfigureAwait(true); var active = await context .CallActivityAsync <bool>(nameof(ProjectCommandMonitoringActivity), notification) .ConfigureAwait(true); if (active) { // monitored orchestration still active - keep monitoring context.ContinueAsNew(notification); } }
public static async Task Run( [OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log) { var data = ctx.GetInput <OrchestratorData>(); if (!ctx.IsReplaying) { log.LogInformation("Orchestration interval {Interval}. Current listeners: {Listeners}", data.Interval, string.Join(", ", data.MusicListeners?.Select(l => l.UserId))); } try { var forceUpdateUsers = data.MusicListeners == null || data.Interval == 0; List <string> listeners; if (forceUpdateUsers) { var users = await ctx.CallActivityAsync <IList <UserEntity> >(FunctionNames.GetAllUsersActivity, null); listeners = users.Select(u => u.RowKey).ToList(); } else { listeners = GetListenersToUpdate(data.MusicListeners, ctx.CurrentUtcDateTime); } List <ListenerTrack> currentListenerTracks; if (listeners.Any()) { currentListenerTracks = await ctx.CallActivityAsync <List <ListenerTrack> >( FunctionNames.GetCurrentTracksActivity, listeners ); } else { currentListenerTracks = new List <ListenerTrack>(); } await ctx.CallActivityAsync( FunctionNames.SignalRTrackActivity, currentListenerTracks ); if (data.MusicListeners == null) { data.MusicListeners = new List <MusicListener>(); } foreach (var playingTrack in currentListenerTracks.Where(t => t.currentTrack?.is_playing == true)) { var listener = data.MusicListeners.FirstOrDefault(m => m.UserId == playingTrack.userId); if (listener != null) { listener.LastSeenActivity = ctx.CurrentUtcDateTime; } else { data.MusicListeners.Add(new MusicListener { UserId = playingTrack.userId, LastSeenActivity = ctx.CurrentUtcDateTime }); } } } catch (Exception ex) { if (!ctx.IsReplaying) { log.LogError(ex, "Failure while retrieving or signalling tracks."); } } DateTime nextIntervalTime; if (data.LastActivity > ctx.CurrentUtcDateTime.AddMinutes(-5)) { nextIntervalTime = ctx.CurrentUtcDateTime.AddSeconds(Config.ActiveListenerIntervalSeconds); } else { nextIntervalTime = ctx.CurrentUtcDateTime.AddSeconds(Config.InactiveListenerIntervalSeconds); } using (var timeoutCts = new CancellationTokenSource()) { Task durableTimeout = ctx.CreateTimer(nextIntervalTime, timeoutCts.Token); Task <bool> wakeUpEvent = ctx.WaitForExternalEvent <bool>(FunctionEvents.WakeUpOrchestrator); var winner = await Task.WhenAny(wakeUpEvent, durableTimeout); if (winner == wakeUpEvent) { timeoutCts.Cancel(); } } if (data.Interval > 2) { data.Interval = 0; } else { data.Interval = data.Interval + 1; } ctx.ContinueAsNew(data); }
public async Task StartOrchestratorScheduler( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { var taskItem = context.GetInput <TaskItem>(); if (taskItem == null) { var error = new ProcessResult { Sucess = false, Information = "Error getting task information" }; AddLog(log, error); return; } TaskTypeEnum taskType = (TaskTypeEnum)taskItem.TaskType; string prepare = ActivityFunctionPrepare(taskType); string process = ActivityFunctionProcess(taskType); string complete = ActivityFunctionComplete(taskType); double secondInAFullDay = 86400; //// hvordan tjekker vi at task'en ikke allerde er scheduleret ? //// i så fald skal den terminineres. var date = context.CurrentUtcDateTime.Hour > taskItem.MonitorHour ? context.CurrentUtcDateTime.AddDays(1) : context.CurrentUtcDateTime; var nextTime = new DateTime(date.Year, date.Month, date.Day, taskItem.MonitorHour, 0, 0); var pollingInterval = nextTime.Subtract(context.CurrentUtcDateTime).TotalSeconds; DateTime expiryTime = new DateTime(3000, 1, 1); while (context.CurrentUtcDateTime < expiryTime) { // Orchestration sleeps until this time. var nextCheck = context.CurrentUtcDateTime.AddSeconds(pollingInterval); await context.CreateTimer(nextCheck, CancellationToken.None); var resultPrepare = await context.CallActivityAsync <ProcessResult>(prepare, taskItem.CustomerId); AddLog(log, resultPrepare); if (resultPrepare.Sucess) { var resultProcess = await context.CallActivityAsync <ProcessResult>(process, taskItem.CustomerId); AddLog(log, resultProcess); if (resultProcess.Sucess) { var resultComplete = await context.CallActivityAsync <ProcessResult>(complete, taskItem.CustomerId); AddLog(log, resultComplete); } } // her sikres at denne uendelig løkke, ikke medfører at durable storage account fyldes op. context.ContinueAsNew(null); // her har man mulighed for at have informationer til næste genneløb pollingInterval = secondInAFullDay; } }
public static async Task <string> MainOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log) { DateTime startTime = context.CurrentUtcDateTime; if (!context.IsReplaying) { log.LogInformation($"Main orchestrator (v{AzTwitterSarVersion.get()}), start time {startTime}. Call activity: GetTweets"); } string lastTweetId = context.GetInput <string>(); // 1) Get tweets from Twitter API List <TweetProcessingData> tweetData = await context.CallActivityAsync <List <TweetProcessingData> >( "A_GetTweets", lastTweetId); // The most likely case is that there are NO new tweets, so we provide // the short circuit which skips all the processing in this block, and // thus avoids a lot of replaying by the durable function mechanism. if (tweetData.Count > 0) { if (!context.IsReplaying) { log.LogInformation($"Got {tweetData.Count} new tweets; enter processing sub-orchestration."); } // 2) Process tweets one by one to find interesting ones. var parallelScoringTasks = new List <Task <TweetProcessingData> >(); foreach (var tpd in tweetData) // processes in order { if (!context.IsReplaying) { log.LogInformation($"Call sub-orchestration: P_ProcessTweet for tweet: {tpd.IdStr}"); } Task <TweetProcessingData> processTask = context.CallSubOrchestratorAsync <TweetProcessingData>( "P_ProcessTweet", tpd); parallelScoringTasks.Add(processTask); } await Task.WhenAll(parallelScoringTasks); // Sort the list of analyzed tweets by ascending id (chronologically). List <TweetProcessingData> logList = (from pt in parallelScoringTasks orderby Int64.Parse(pt.Result.IdStr) select pt.Result).ToList(); // Find the tweets that shall be published, chronological order. List <TweetProcessingData> publishList = ( from tpd in logList where tpd.ShallBePublished orderby Int64.Parse(tpd.IdStr) select tpd).ToList(); // Parallel section for postprocessing tasks. { if (!context.IsReplaying) { log.LogInformation($"Publishing {publishList.Count} tweets; logging {logList.Count} tweets."); } List <Task <int> > parallelPostprocessingTasks = new List <Task <int> >(); // We know there is something in the log list, but publishing we // trigger only if there is something to do for this activity. if (publishList.Count > 0) { parallelPostprocessingTasks.Add(context.CallActivityAsync <int>("A_PublishTweets", publishList)); } parallelPostprocessingTasks.Add(context.CallActivityAsync <int>("A_LogTweets", logList)); await Task.WhenAll(parallelPostprocessingTasks); } // We know there has been >= 1 tweet, so we update the last seen id, // which is passed to the next call. lastTweetId = logList[logList.Count - 1].IdStr; } else { if (!context.IsReplaying) { log.LogInformation($"Got no new tweets."); } } DateTime currentTime = context.CurrentUtcDateTime; int delaySeconds = GetDelaySeconds(context, log, startTime, currentTime); if (!context.IsReplaying) { log.LogInformation($"Determined delay: {delaySeconds} seconds after current time {currentTime}."); } if (delaySeconds > 0) { DateTime nextTime = currentTime.AddSeconds(delaySeconds); if (!context.IsReplaying) { log.LogInformation($"Setting wakeup time: {nextTime}."); } await context.CreateTimer(nextTime, CancellationToken.None); context.ContinueAsNew(lastTweetId); } return(lastTweetId); }