public SharedBlobListener(IStorageAccount storageAccount, IBackgroundExceptionDispatcher backgroundExceptionDispatcher) { _strategy = CreateStrategy(storageAccount); // Start the first iteration immediately. _timer = new TaskSeriesTimer(_strategy, backgroundExceptionDispatcher, initialWait: Task.Delay(0)); }
public void Dispose_IfStarted_DoesNotWaitForExecuteToFinish() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle stopExecute = new ManualResetEvent(initialState: false)) { bool waitedForCommandToFinish = false; ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executeStarted.Set()); // Guard stopExecute.WaitOne(1000); waitedForCommandToFinish = true; return(new TaskSeriesCommandResult(wait: Task.Delay(0))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard // Act & Assert product.Dispose(); // Assert Assert.False(waitedForCommandToFinish); // Cleanup Assert.True(stopExecute.Set()); // Guard } } }
public void Start_AfterInitialWait_Executes() { // Arrange // Detect the difference between waiting and not waiting, but keep the test execution time fast. TimeSpan initialDelay = TimeSpan.FromMilliseconds(50); using (EventWaitHandle executedWaitHandle = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executedWaitHandle.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(TimeSpan.FromDays(1)))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command, Task.Delay(initialDelay))) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // Act product.Start(); // Assert Assert.True(executedWaitHandle.WaitOne(1000)); // Guard stopwatch.Stop(); // Account for the resolution of the system timer; otherwise, the test may fail intermittently // (as the timer may fire slightly before the precise expected value). TimeSpan effectiveActualDelay = AddSystemTimerResolution(stopwatch.Elapsed); AssertGreaterThan(initialDelay, effectiveActualDelay); } } }
public HeartbeatListener(IRecurrentCommand heartbeatCommand, IWebJobsExceptionHandler exceptionHandler, IListener innerListener) { _heartbeatCommand = heartbeatCommand; _innerListener = innerListener; _timer = CreateTimer(exceptionHandler); }
public SharedBlobListener(string hostId, StorageAccount storageAccount, IWebJobsExceptionHandler exceptionHandler) { _strategy = CreateStrategy(hostId, storageAccount); // Start the first iteration immediately. _timer = new TaskSeriesTimer(_strategy, exceptionHandler, initialWait: Task.Delay(0)); }
public void StopAsync_TriggersCommandCancellationToken() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle executeFinished = new ManualResetEvent(initialState: false)) { bool cancellationTokenSignalled = false; ITaskSeriesCommand command = CreateCommand((cancellationToken) => { Assert.True(executeStarted.Set()); // Guard cancellationTokenSignalled = cancellationToken.WaitHandle.WaitOne(1000); Assert.True(executeFinished.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(0))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard CancellationToken cancellationToken = CancellationToken.None; // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); task.GetAwaiter().GetResult(); Assert.True(executeFinished.WaitOne(1000)); // Guard Assert.True(cancellationTokenSignalled); } } }
public void StopAsync_WhenCanceled_DoesNotWaitForExecuteToFinishToCompleteTask() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executeStarted.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(2000))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard CancellationToken cancellationToken = new CancellationToken(canceled: true); // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); Assert.True(task.WaitUntilCompleted(1000)); Assert.True(task.IsCompleted); } } }
internal async Task ProcessMessageAsync(IStorageQueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { if (!await _queueProcessor.BeginProcessingMessageAsync(message.SdkObject, cancellationToken)) { return; } FunctionResult result = null; using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _backgroundExceptionDispatcher)) { timer.Start(); result = await _triggerExecutor.ExecuteAsync(message, cancellationToken); await timer.StopAsync(cancellationToken); } await _queueProcessor.CompleteProcessingMessageAsync(message.SdkObject, result, cancellationToken); } catch (OperationCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (Exception exception) { // Immediately report any unhandled exception from this background task. // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until // Stop is called, which might never happen.) _backgroundExceptionDispatcher.Throw(ExceptionDispatchInfo.Capture(exception)); } }
public HeartbeatListener(IRecurrentCommand heartbeatCommand, IBackgroundExceptionDispatcher backgroundExceptionDispatcher, IListener innerListener) { _heartbeatCommand = heartbeatCommand; _innerListener = innerListener; _timer = CreateTimer(backgroundExceptionDispatcher); }
public void StopAsync_DoesNotWaitForSubsequentWaitToCompleteTask() { // Arrange using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { Assert.True(executeStarted.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(2000))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard // Wait for the background thread to enter the long wait. Thread.Sleep(5); CancellationToken cancellationToken = CancellationToken.None; // Act Task task = product.StopAsync(cancellationToken); // Assert Assert.NotNull(task); Assert.True(task.WaitUntilCompleted(1000)); Assert.True(task.IsCompleted); } } }
private async Task ExecuteWithOutputLogsAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TextWriter consoleOutput, IFunctionOutputDefinition outputDefinition, IDictionary <string, ParameterLog> parameterLogCollector, CancellationToken cancellationToken) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyDictionary <string, IWatcher> watches = CreateWatches(parameters); IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(watches, consoleOutput); using (ITaskSeriesTimer updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _backgroundExceptionDispatcher)) { try { await ExecuteWithWatchersAsync(invoker, parameters, cancellationToken); if (updateParameterLogTimer != null) { // Stop the watches after calling IValueBinder.SetValue (it may do things that should show up in // the watches). // Also, IValueBinder.SetValue could also take a long time (flushing large caches), and so it's // useful to have watches still running. await updateParameterLogTimer.StopAsync(cancellationToken); } } finally { ValueWatcher.AddLogs(watches, parameterLogCollector); } } }
public void Dispose_TriggersCommandCancellationToken() { // Arrange TimeSpan interval = TimeSpan.Zero; using (EventWaitHandle executeStarted = new ManualResetEvent(initialState: false)) using (EventWaitHandle executeFinished = new ManualResetEvent(initialState: false)) { bool cancellationTokenSignalled = false; ITaskSeriesCommand command = CreateCommand((cancellationToken) => { Assert.True(executeStarted.Set()); // Guard cancellationTokenSignalled = cancellationToken.WaitHandle.WaitOne(1000); Assert.True(executeFinished.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(0))); }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); Assert.True(executeStarted.WaitOne(1000)); // Guard // Act product.Dispose(); // Assert Assert.True(executeFinished.WaitOne(1000)); // Guard Assert.True(cancellationTokenSignalled); } } }
public QueueListener(IStorageQueue queue, IStorageQueue poisonQueue, ITriggerExecutor <IStorageQueueMessage> triggerExecutor, IWebJobsExceptionHandler exceptionHandler, ILoggerFactory loggerFactory, SharedQueueWatcher sharedWatcher, IQueueConfiguration queueConfiguration, QueueProcessor queueProcessor = null, TimeSpan?maxPollingInterval = null) { if (queueConfiguration == null) { throw new ArgumentNullException("queueConfiguration"); } if (queueConfiguration.BatchSize <= 0) { throw new ArgumentException("BatchSize must be greater than zero."); } if (queueConfiguration.MaxDequeueCount <= 0) { throw new ArgumentException("MaxDequeueCount must be greater than zero."); } _timer = new TaskSeriesTimer(this, exceptionHandler, Task.Delay(0)); _queue = queue; _poisonQueue = poisonQueue; _triggerExecutor = triggerExecutor; _exceptionHandler = exceptionHandler; _queueConfiguration = queueConfiguration; // if the function runs longer than this, the invisibility will be updated // on a timer periodically for the duration of the function execution _visibilityTimeout = TimeSpan.FromMinutes(10); if (sharedWatcher != null) { // Call Notify whenever a function adds a message to this queue. sharedWatcher.Register(queue.Name, this); _sharedWatcher = sharedWatcher; } EventHandler <PoisonMessageEventArgs> poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler <PoisonMessageEventArgs>)null; _queueProcessor = queueProcessor ?? CreateQueueProcessor( _queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null, loggerFactory, _queueConfiguration, poisonMessageEventHandler); TimeSpan maximumInterval = _queueProcessor.MaxPollingInterval; if (maxPollingInterval.HasValue && maximumInterval > maxPollingInterval.Value) { // enforce the maximum polling interval if specified maximumInterval = maxPollingInterval.Value; } _delayStrategy = new RandomizedExponentialBackoffStrategy(QueuePollingIntervals.Minimum, maximumInterval); }
// Begin watchers. public ValueWatcher(IReadOnlyDictionary<string, IWatcher> watches, CloudBlockBlob blobResults, TextWriter consoleOutput, IBackgroundExceptionDispatcher backgroundExceptionDispatcher) { ValueWatcherCommand command = new ValueWatcherCommand(watches, blobResults, consoleOutput); _command = command; _timer = ValueWatcherCommand.CreateTimer(command, backgroundExceptionDispatcher); _timer.Start(); }
public static RenewableLockHandle CreateRenewableLockHandle(TimeSpan leasePeriod, IDistributedLockManager distributedLockManager, IDistributedLock lockHandle, Func <bool> preExecuteCheck) { ITaskSeriesTimer renewal = CreateLeaseRenewalTimer(leasePeriod, distributedLockManager, lockHandle, preExecuteCheck); renewal.Start(); return(new RenewableLockHandle(lockHandle, renewal)); }
// Begin watchers. public ValueWatcher(IReadOnlyDictionary <string, IWatcher> watches, CloudBlockBlob blobResults, TextWriter consoleOutput, IBackgroundExceptionDispatcher backgroundExceptionDispatcher) { ValueWatcherCommand command = new ValueWatcherCommand(watches, blobResults, consoleOutput); _command = command; _timer = ValueWatcherCommand.CreateTimer(command, backgroundExceptionDispatcher); _timer.Start(); }
public void Dispose_IfNotStarted_DoesNotThrow() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); ITaskSeriesTimer product = CreateProductUnderTest(command); // Act & Assert ExceptionAssert.DoesNotThrow(() => product.Dispose()); }
private async Task ProcessMessageAsync(IStorageQueueMessage message, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { bool succeeded; using (ITaskSeriesTimer timer = CreateUpdateMessageVisibilityTimer(_queue, message, visibilityTimeout, _backgroundExceptionDispatcher)) { timer.Start(); succeeded = await _triggerExecutor.ExecuteAsync(message, cancellationToken); await timer.StopAsync(cancellationToken); } // Need to call Delete message only if function succeeded. if (succeeded) { await DeleteMessageAsync(message, cancellationToken); } else if (_poisonQueue != null) { if (message.DequeueCount >= _maxDequeueCount) { _log.WriteLine("Message has reached MaxDequeueCount of {0}. Moving message to queue '{1}'.", _maxDequeueCount, _poisonQueue.Name); await CopyToPoisonQueueAsync(message, cancellationToken); await DeleteMessageAsync(message, cancellationToken); } else { await ReleaseMessageAsync(message, cancellationToken); } } else { // For queues without a corresponding poison queue, leave the message invisible when processing // fails to prevent a fast infinite loop. // Specifically, don't call ReleaseMessage(message) } } catch (OperationCanceledException) { // Don't fail the top-level task when an inner task cancels. } catch (Exception exception) { // Immediately report any unhandled exception from this background task. // (Don't capture the exception as a fault of this Task; that would delay any exception reporting until // Stop is called, which might never happen.) _backgroundExceptionDispatcher.Throw(ExceptionDispatchInfo.Capture(exception)); } }
public void Cancel_IfDisposed_Throws() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); ITaskSeriesTimer product = CreateProductUnderTest(command); product.Dispose(); // Act & Assert ExceptionAssert.ThrowsObjectDisposed(() => product.Cancel()); }
public void Dispose_IfStopped_DoesNotThrow() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.Zero); ITaskSeriesTimer product = CreateProductUnderTest(command); product.Start(); product.StopAsync(CancellationToken.None).GetAwaiter().GetResult(); // Act & Assert ExceptionAssert.DoesNotThrow(() => product.Dispose()); }
public void StopAsync_IfDisposed_Throws() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); ITaskSeriesTimer product = CreateProductUnderTest(command); product.Dispose(); CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsObjectDisposed(() => product.StopAsync(cancellationToken)); }
public QueueListener(IStorageQueue queue, IStorageQueue poisonQueue, ITriggerExecutor <IStorageQueueMessage> triggerExecutor, IDelayStrategy delayStrategy, IBackgroundExceptionDispatcher backgroundExceptionDispatcher, TraceWriter trace, SharedQueueWatcher sharedWatcher, IQueueConfiguration queueConfiguration) { if (trace == null) { throw new ArgumentNullException("trace"); } if (queueConfiguration == null) { throw new ArgumentNullException("queueConfiguration"); } if (queueConfiguration.BatchSize <= 0) { throw new ArgumentException("BatchSize must be greater than zero."); } if (queueConfiguration.MaxDequeueCount <= 0) { throw new ArgumentException("MaxDequeueCount must be greater than zero."); } _timer = new TaskSeriesTimer(this, backgroundExceptionDispatcher, Task.Delay(0)); _queue = queue; _poisonQueue = poisonQueue; _triggerExecutor = triggerExecutor; _delayStrategy = delayStrategy; _backgroundExceptionDispatcher = backgroundExceptionDispatcher; _trace = trace; _queueConfiguration = queueConfiguration; if (sharedWatcher != null) { // Call Notify whenever a function adds a message to this queue. sharedWatcher.Register(queue.Name, this); _sharedWatcher = sharedWatcher; } EventHandler poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler)null; _queueProcessor = CreateQueueProcessor( _queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null, _trace, _queueConfiguration, poisonMessageEventHandler); }
public void StopAsync_IfNotStarted_Throws() { // Arrange ITaskSeriesCommand command = CreateDummyCommand(); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsInvalidOperation(() => product.StopAsync(cancellationToken), "The timer has not yet been started."); } }
public QueueListener(IStorageQueue queue, IStorageQueue poisonQueue, ITriggerExecutor<IStorageQueueMessage> triggerExecutor, IDelayStrategy delayStrategy, IBackgroundExceptionDispatcher backgroundExceptionDispatcher, TraceWriter trace, SharedQueueWatcher sharedWatcher, IQueueConfiguration queueConfiguration) { if (trace == null) { throw new ArgumentNullException("trace"); } if (queueConfiguration == null) { throw new ArgumentNullException("queueConfiguration"); } if (queueConfiguration.BatchSize <= 0) { throw new ArgumentException("BatchSize must be greater than zero."); } if (queueConfiguration.MaxDequeueCount <= 0) { throw new ArgumentException("MaxDequeueCount must be greater than zero."); } _timer = new TaskSeriesTimer(this, backgroundExceptionDispatcher, Task.Delay(0)); _queue = queue; _poisonQueue = poisonQueue; _triggerExecutor = triggerExecutor; _delayStrategy = delayStrategy; _backgroundExceptionDispatcher = backgroundExceptionDispatcher; _trace = trace; _queueConfiguration = queueConfiguration; if (sharedWatcher != null) { // Call Notify whenever a function adds a message to this queue. sharedWatcher.Register(queue.Name, this); _sharedWatcher = sharedWatcher; } EventHandler poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler)null; _queueProcessor = CreateQueueProcessor( _queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null, _trace, _queueConfiguration, poisonMessageEventHandler); }
public void Start_IfStarted_Throws() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.FromDays(1)); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); // Act & Assert ExceptionAssert.ThrowsInvalidOperation(() => product.Start(), "The timer has already been started; it cannot be restarted."); } }
private static ITaskSeriesTimer StartOutputTimer(IRecurrentCommand updateCommand, IBackgroundExceptionDispatcher backgroundExceptionDispatcher) { if (updateCommand == null) { return(null); } TimeSpan initialDelay = FunctionOutputIntervals.InitialDelay; TimeSpan refreshRate = FunctionOutputIntervals.RefreshRate; ITaskSeriesTimer timer = FixedDelayStrategy.CreateTimer(updateCommand, initialDelay, refreshRate, backgroundExceptionDispatcher); timer.Start(); return(timer); }
private static ITaskSeriesTimer StartParameterLogTimer(IRecurrentCommand updateCommand, IWebJobsExceptionHandler exceptionHandler) { if (updateCommand == null) { return(null); } TimeSpan initialDelay = FunctionParameterLogIntervals.InitialDelay; TimeSpan refreshRate = FunctionParameterLogIntervals.RefreshRate; ITaskSeriesTimer timer = FixedDelayStrategy.CreateTimer(updateCommand, initialDelay, refreshRate, exceptionHandler); timer.Start(); return(timer); }
private async Task ExecuteWithLoggingAsync(IFunctionInstance instance, IReadOnlyDictionary <string, IValueProvider> parameters, TraceWriter trace, ILogger logger, IFunctionOutputDefinition outputDefinition, IDictionary <string, ParameterLog> parameterLogCollector, TraceLevel functionTraceLevel, CancellationTokenSource functionCancellationTokenSource) { IFunctionInvoker invoker = instance.Invoker; IReadOnlyDictionary <string, IWatcher> parameterWatchers = null; ITaskSeriesTimer updateParameterLogTimer = null; if (functionTraceLevel >= TraceLevel.Info) { parameterWatchers = CreateParameterWatchers(parameters); IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace, logger); updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _exceptionHandler); } try { await ExecuteWithWatchersAsync(instance, parameters, trace, logger, functionCancellationTokenSource); if (updateParameterLogTimer != null) { // Stop the watches after calling IValueBinder.SetValue (it may do things that should show up in // the watches). // Also, IValueBinder.SetValue could also take a long time (flushing large caches), and so it's // useful to have watches still running. await updateParameterLogTimer.StopAsync(functionCancellationTokenSource.Token); } } finally { if (updateParameterLogTimer != null) { ((IDisposable)updateParameterLogTimer).Dispose(); } if (parameterWatchers != null) { ValueWatcher.AddLogs(parameterWatchers, parameterLogCollector); } } }
public async Task <IDelayedException> TryExecuteAsync(IFunctionInstance instance, CancellationToken cancellationToken) { IDelayedException result; using (ITaskSeriesTimer timer = CreateHeartbeatTimer(_exceptionHandler)) { await _heartbeatCommand.TryExecuteAsync(cancellationToken); timer.Start(); result = await _innerExecutor.TryExecuteAsync(instance, cancellationToken); await timer.StopAsync(cancellationToken); } return(result); }
public void StopAsync_IfAlreadyStopped_Throws() { // Arrange ITaskSeriesCommand command = CreateStubCommand(TimeSpan.Zero); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { product.Start(); product.StopAsync(CancellationToken.None).GetAwaiter().GetResult(); CancellationToken cancellationToken = CancellationToken.None; // Act & Assert ExceptionAssert.ThrowsInvalidOperation(() => product.StopAsync(cancellationToken), "The timer has already been stopped."); } }
public void Start_AfterExecute_WaitsForReturnedWait() { // Arrange bool executedOnce = false; bool executedTwice = false; TimeSpan initialInterval = TimeSpan.Zero; // Detect the difference between waiting and not waiting, but keep the test execution time fast. TimeSpan subsequentInterval = TimeSpan.FromMilliseconds(5); Stopwatch stopwatch = new Stopwatch(); using (EventWaitHandle waitForSecondExecution = new ManualResetEvent(initialState: false)) { ITaskSeriesCommand command = CreateCommand(() => { if (executedTwice) { return(new TaskSeriesCommandResult(wait: Task.Delay(TimeSpan.FromDays(1)))); } if (!executedOnce) { stopwatch.Start(); executedOnce = true; return(new TaskSeriesCommandResult(wait: Task.Delay(subsequentInterval))); } else { stopwatch.Stop(); executedTwice = true; Assert.True(waitForSecondExecution.Set()); // Guard return(new TaskSeriesCommandResult(wait: Task.Delay(initialInterval))); } }); using (ITaskSeriesTimer product = CreateProductUnderTest(command)) { // Act product.Start(); // Assert Assert.True(waitForSecondExecution.WaitOne(1000)); // Guard AssertGreaterThan(subsequentInterval, stopwatch.Elapsed); } } }
public SharedBlobListener(string hostId, BlobServiceClient blobServiceClient, IWebJobsExceptionHandler exceptionHandler, ILogger <BlobListener> logger) { if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (exceptionHandler == null) { throw new ArgumentNullException(nameof(exceptionHandler)); } _strategy = CreateStrategy(hostId, blobServiceClient, exceptionHandler, logger); // Start the first iteration immediately. _timer = new TaskSeriesTimer(_strategy, exceptionHandler, initialWait: Task.Delay(0)); }
public TimerListener(ITaskSeriesTimer timer) { _timer = timer; }