public void ExceptionFunc(Exception exc, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, exc); }
public void ArrayFunc2(int[] nums, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, string.Join(",", nums)); }
public void BoolFunc(bool num, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num); }
public void DoubleFunc(double num, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num); }
public void NullableTypeFunc(DateTime?dateTime, string semaphoreFile) { TaskQueueTestFixture.WriteSempaphoreValue(semaphoreFile, dateTime); }
public void LongFunc(long num, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num); }
public void ObjectFunc(object num1, object num2, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num2); }
public void IntFunc(int num, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num); }
public async Task ItExecutesScheduledTasksOnlyOnce() { const int numberOfTasks = 1000; const int tasksPerTimeInterval = 25; const int timeInterval = 1000; const int numberOfSchedulerThreads = 4; TimeSpan promotionFrequency = TimeSpan.FromMilliseconds(100); ThreadPool.SetMinThreads(numberOfSchedulerThreads * 4, 200); var taskQueue = TaskQueueTestFixture.UniqueRedisTaskQueue(); var taskScheduler = new TaskScheduler(taskQueue, promotionFrequency); var canceled = false; var taskSchedulerTasks = new List <Task>(); var taskRunnerTasks = new List <Task>(); for (var s = 0; s < numberOfSchedulerThreads; ++s) { taskSchedulerTasks.Add(Task.Run(async() => { var inThreadTaskScheduler = new TaskScheduler(taskQueue); while (!canceled) { await inThreadTaskScheduler.Tick(forceRunPromotion: true); } })); taskRunnerTasks.Add(Task.Run(async() => { while (!canceled) { await taskQueue.ExecuteNext(); } })); } var tasks = new List <(ScheduledTask, string, string)>(); System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); for (var i = 0; i < numberOfTasks / tasksPerTimeInterval; ++i) { for (var k = 0; k < tasksPerTimeInterval; ++k) { var semaphoreFile = Path.GetTempFileName(); File.Delete(semaphoreFile); semaphoreFile = semaphoreFile + Guid.NewGuid().ToString(); var semaphoreValue = Guid.NewGuid().ToString(); var scheduledTask = await taskScheduler.AddScheduledTask( () => TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, semaphoreValue), TimeSpan.FromMilliseconds((i + 1) * timeInterval)); tasks.Add((scheduledTask, semaphoreFile, semaphoreValue)); } } _testOutputHelper.WriteLine(sw.ElapsedMilliseconds.ToString()); var precisionAllowanceFactorMs = promotionFrequency.TotalMilliseconds + 10; long now; for (var i = 0; i < numberOfTasks / tasksPerTimeInterval; ++i) { Thread.Sleep(timeInterval); // EACH TASK, 3 Cases // CASE 1: Scheduled Time should have run, allowing for precision loss // CASE 2: Scheduled Time should definitely not have run // CASE 3: Scheduled Time is past, but within precision allowance (may have run or not) foreach (var task in tasks) { now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); if (task.Item1.ScheduledUnixTimeMilliseconds <= (now - precisionAllowanceFactorMs)) { File.Exists(task.Item2).Should().BeTrue(); File.ReadAllText(task.Item2).Should().Be(task.Item3); } else if (task.Item1.ScheduledUnixTimeMilliseconds > now) { File.Exists(task.Item2).Should().BeFalse(); } } } Thread.Sleep((int)precisionAllowanceFactorMs); foreach (var task in tasks) { File.Exists(task.Item2).Should().BeTrue(); File.ReadAllText(task.Item2).Should().Be(task.Item3); } canceled = true; await Task.WhenAll(taskSchedulerTasks); await Task.WhenAll(taskRunnerTasks); }
public async Task ItExecutesTasksOnlyOnceWhenUsingMultipleConsumers(string taskType) { var semaphoreFile = Path.GetTempFileName(); File.Delete(semaphoreFile); File.Create(semaphoreFile).Close(); var intervalSeconds = 5; var taskQueue = TaskQueueTestFixture.UniqueRedisTaskQueue(); var schedulingTaskScheduler = new TaskScheduler(taskQueue); var taskSchedulers = new[] { new TaskScheduler(taskQueue), new TaskScheduler(taskQueue), new TaskScheduler(taskQueue), new TaskScheduler(taskQueue), }; if (taskType == "scheduled") { await schedulingTaskScheduler.AddScheduledTask(() => TaskQueueTestFixture.WriteSemaphore(semaphoreFile), TimeSpan.FromSeconds(intervalSeconds)); } if (taskType == "recurring") { await schedulingTaskScheduler.AddRecurringTask(() => TaskQueueTestFixture.WriteSemaphore(semaphoreFile), TimeSpan.FromSeconds(intervalSeconds), RandomTaskName); } Thread.Sleep(((intervalSeconds) * 1000) + 50); // Ran only once await Task.WhenAll( taskSchedulers.Select( taskScheduler => Task.Run(async() => { await taskScheduler.Tick(); await taskQueue.ExecuteNext(); }))); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Ran only twice (or once if scheduled) Thread.Sleep(((intervalSeconds) * 1000) + 50); await Task.WhenAll( taskSchedulers.Select( taskScheduler => Task.Run(async() => { await taskScheduler.Tick(); await taskQueue.ExecuteNext(); }))); if (taskType == "recurring") { File.ReadAllText(semaphoreFile).Should() .Be(TaskQueueTestFixture.SemaphoreText + TaskQueueTestFixture.SemaphoreText); } if (taskType == "scheduled") { File.ReadAllText(semaphoreFile).Should() .Be(TaskQueueTestFixture.SemaphoreText); } }
public async Task ItExecutesCrontabRecurringTasksAccordingToTheirSchedules() { const int numberOfTasks = 10; const int timeInterval = 1000; const int testIntervals = 10; const int numberOfSchedulerThreads = 4; const int precisionAllowanceFactorMs = 800; ThreadPool.SetMinThreads(numberOfSchedulerThreads * 4, 200); var taskQueue = TaskQueueTestFixture.UniqueRedisTaskQueue(); var taskScheduler = new TaskScheduler(taskQueue); var canceled = false; var taskSchedulerTasks = new List <Task>(); var taskRunnerTasks = new List <Task>(); for (var s = 0; s < numberOfSchedulerThreads; ++s) { taskSchedulerTasks.Add(Task.Run(async() => { var inThreadTaskScheduler = new TaskScheduler(taskQueue); while (!canceled) { await inThreadTaskScheduler.Tick(forceRunPromotion: true); } })); taskRunnerTasks.Add(Task.Run(async() => { while (!canceled) { await taskQueue.ExecuteNext(); } })); } var tasks = new List <(RecurringTask, string, string)>(); for (var i = 0; i < numberOfTasks; ++i) { var semaphoreFile = Path.GetTempFileName(); File.Delete(semaphoreFile); semaphoreFile = semaphoreFile + Guid.NewGuid().ToString(); var semaphoreValue = Guid.NewGuid().ToString(); var recurringTask = await taskScheduler.AddRecurringTask( () => TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, semaphoreValue), $"*/{timeInterval / 1000} * * * * *", Guid.NewGuid().ToString()); tasks.Add((recurringTask, semaphoreFile, semaphoreValue)); } var synchronizationFactor = DateTime.UtcNow.ToUnixTimeMilliseconds() - DateTime.UtcNow.ToUnixTimeSeconds() * 1000; Thread.Sleep((int)synchronizationFactor + 100); for (var i = 0; i < testIntervals; ++i) { Thread.Sleep(timeInterval); foreach (var task in tasks) { var diff = DateTime.UtcNow.ToUnixTimeMilliseconds() - (task.Item1.FirstRunTime.ToUnixTimeMilliseconds() - timeInterval); var elapsedIntervalsMax = diff / timeInterval; var elapsedIntervalsMin = (diff % timeInterval) <= precisionAllowanceFactorMs ? elapsedIntervalsMax - 1 : elapsedIntervalsMax; _testOutputHelper.WriteLine(diff.ToString()); elapsedIntervalsMax.Should().BeGreaterThan(i); var expectedStringMax = ""; for (var e = 0; e < elapsedIntervalsMax; ++e) { expectedStringMax += task.Item3; } var expectedStringMin = ""; for (var e = 0; e < elapsedIntervalsMin; ++e) { expectedStringMin += task.Item3; } File.Exists(task.Item2).Should().BeTrue(elapsedIntervalsMax.ToString()); var fileText = File.ReadAllText(task.Item2); if (fileText != expectedStringMax) { fileText.Should().Be(expectedStringMin); } } } canceled = true; await Task.WhenAll(taskSchedulerTasks); await Task.WhenAll(taskRunnerTasks); }
public void ItCapturesArgumentsPassedToEnqueuedDelegate() { var testFixture = new TaskQueueTestFixture(nameof(ItCapturesArgumentsPassedToEnqueuedDelegate)); string variableToExtract = "extracted"; var semaphoreFile = Path.GetTempFileName(); var now = DateTime.Now; var utcNow = DateTime.UtcNow; Func <Expression <Action>, string, Tuple <Expression <Action>, string> > TC = (actionExp, str) => Tuple.Create <Expression <Action>, string>( actionExp, str); // Action to expected result var delgates = new Tuple <Expression <Action>, string>[] { // Integer Arguments TC(() => IntFunc(int.MaxValue, semaphoreFile), int.MaxValue.ToString()), TC(() => IntFunc(int.MinValue, semaphoreFile), int.MinValue.ToString()), TC(() => NullableIntFunc(null, semaphoreFile), "-1"), TC(() => NullableIntFunc(int.MinValue, semaphoreFile), int.MinValue.ToString()), TC(() => NullableIntFunc(int.MaxValue, semaphoreFile), int.MaxValue.ToString()), // Float Arguments TC(() => FloatFunc(float.MaxValue, semaphoreFile), float.MaxValue.ToString()), TC(() => FloatFunc(float.MinValue, semaphoreFile), float.MinValue.ToString()), // Double Arguments TC(() => DoubleFunc(double.MaxValue, semaphoreFile), double.MaxValue.ToString()), TC(() => DoubleFunc(double.MinValue, semaphoreFile), double.MinValue.ToString()), // Long Arguments TC(() => LongFunc(long.MaxValue, semaphoreFile), long.MaxValue.ToString()), TC(() => LongFunc(long.MinValue, semaphoreFile), long.MinValue.ToString()), TC(() => BoolFunc(true, semaphoreFile), true.ToString()), TC(() => BoolFunc(false, semaphoreFile), false.ToString()), TC(() => StringFunc("astring", semaphoreFile), "astring"), TC(() => StringFunc(variableToExtract, semaphoreFile), variableToExtract), TC(() => ObjectFunc(new TestDataHolder { Value = "astring" }, semaphoreFile), "astring"), TC(() => DateTimeFunc(now, semaphoreFile), now.ToString()), TC(() => DateTimeFunc(utcNow, semaphoreFile), utcNow.ToString()), TC(() => NullableTypeFunc(null, semaphoreFile), "null"), TC(() => NullableTypeFunc(now, semaphoreFile), now.ToString()), TC(() => ArrayFunc1(new[] { "this", "string", "is" }, semaphoreFile), "this,string,is"), TC(() => ArrayFunc2(new[] { 1, 2, 3, 4 }, semaphoreFile), "1,2,3,4"), TC(() => ArrayFunc3(new int?[] { 1, 2, 3, null, 5 }, semaphoreFile), "1,2,3,null,5") }; foreach (var tup in delgates) { var actionExpr = tup.Item1; var expectedString = tup.Item2; File.Delete(semaphoreFile); testFixture.TaskQueue.Enqueue(actionExpr); testFixture.TaskQueue.ExecuteNext(); File.ReadAllText(semaphoreFile).Should().Be(expectedString); } File.Delete(semaphoreFile); }
public void FloatFunc(float num, string semaphoreFile) { TaskQueueTestFixture.WriteSempaphoreValue(semaphoreFile, num); }
public async Task ItCapturesArgumentsPassedToEnqueuedDelegate() { var testFixture = new TaskQueueTestFixture(nameof(ItCapturesArgumentsPassedToEnqueuedDelegate)); string variableToExtract = "extracted"; var semaphoreFile = Path.GetTempFileName(); var now = DateTime.Now; var utcNow = DateTime.UtcNow; Func <Expression <Action>, string, Tuple <Expression <Action>, string> > TC = (actionExp, str) => Tuple.Create <Expression <Action>, string>( actionExp, str); // Action to expected result var delgates = new Tuple <Expression <Action>, string>[] { // Exception Argument TC(() => ExceptionFunc(new Exception(), semaphoreFile), new Exception().ToString()), TC(() => ExceptionFunc(new CustomException(), semaphoreFile), new CustomException().ToString()), // Integer Arguments TC(() => IntFunc(int.MaxValue, semaphoreFile), int.MaxValue.ToString()), TC(() => IntFunc(int.MinValue, semaphoreFile), int.MinValue.ToString()), TC(() => NullableIntFunc(null, semaphoreFile), "-1"), TC(() => NullableIntFunc(int.MinValue, semaphoreFile), int.MinValue.ToString()), TC(() => NullableIntFunc(int.MaxValue, semaphoreFile), int.MaxValue.ToString()), // Float Arguments TC(() => FloatFunc(float.MaxValue, semaphoreFile), float.MaxValue.ToString()), TC(() => FloatFunc(float.MinValue, semaphoreFile), float.MinValue.ToString()), // Double Arguments TC(() => DoubleFunc(double.MaxValue, semaphoreFile), double.MaxValue.ToString()), TC(() => DoubleFunc(double.MinValue, semaphoreFile), double.MinValue.ToString()), // Long Arguments TC(() => LongFunc(long.MaxValue, semaphoreFile), long.MaxValue.ToString()), TC(() => LongFunc(long.MinValue, semaphoreFile), long.MinValue.ToString()), TC(() => BoolFunc(true, semaphoreFile), true.ToString()), TC(() => BoolFunc(false, semaphoreFile), false.ToString()), TC(() => StringFunc("astring", semaphoreFile), "astring"), TC(() => StringFunc(variableToExtract, semaphoreFile), variableToExtract), TC(() => ObjectFunc(new TestDataHolder { Value = "astring" }, semaphoreFile), "astring"), TC(() => DateTimeFunc(now, semaphoreFile), now.ToString()), TC(() => DateTimeFunc(utcNow, semaphoreFile), utcNow.ToString()), TC(() => NullableTypeFunc(null, semaphoreFile), "null"), TC(() => NullableTypeFunc(now, semaphoreFile), now.ToString()), TC(() => ArrayFunc1(new[] { "this", "string", "is" }, semaphoreFile), "this,string,is"), TC(() => ArrayFunc2(new[] { 1, 2, 3, 4 }, semaphoreFile), "1,2,3,4"), TC(() => ArrayFunc3(new int?[] { 1, 2, 3, null, 5 }, semaphoreFile), "1,2,3,null,5"), // Awaiting inside the lambda is unnecessary, as the method is extracted and serialized. #pragma warning disable 4014 TC(() => AsyncFunc(semaphoreFile), "async"), TC(() => AsyncFuncThatReturnsString(semaphoreFile), "async") #pragma warning restore 4014 }; foreach (var tup in delgates) { var actionExpr = tup.Item1; var expectedString = tup.Item2; File.Delete(semaphoreFile); await testFixture.TaskQueue.Enqueue(actionExpr); await testFixture.TaskQueue.ExecuteNext(); File.ReadAllText(semaphoreFile).Should().Be(expectedString); } File.Delete(semaphoreFile); }
public void TypeFunc(Type typeArg, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, typeArg?.ToString() ?? "null"); }
public void DateTimeFunc(DateTime dateTime, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, dateTime); }
internal static async Task AssertTaskSchedulerWritesSemaphoreTwiceAfterReconfiguringInterval( int intervalSeconds, Func <string, TaskScheduler, Task> configureSchedulerAction, Func <string, TaskScheduler, Task> reconfigureRecurringTaskIntervalAction) { var semaphoreFile = Path.GetTempFileName(); File.Delete(semaphoreFile); var taskQueue = TaskQueueTestFixture.UniqueRedisTaskQueue(); var taskScheduler = new TaskScheduler(taskQueue); await configureSchedulerAction(semaphoreFile, taskScheduler); await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(false); // Confirm Scheduled Task Ran once Thread.Sleep((((intervalSeconds) * 1000) + 10)); await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Confirm does not run before interval elapsed await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Reconfigure interval to 2 * IntervalSeconds await reconfigureRecurringTaskIntervalAction(semaphoreFile, taskScheduler); // Confirm does not run before interval elapsed await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Sleep previous interval & confirm does not run before interval elapsed Thread.Sleep((((intervalSeconds) * 1000) + 10)); await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Confirm Ran Twice Thread.Sleep((((intervalSeconds) * 1000) + 10)); await taskScheduler.Tick(); await taskQueue.ExecuteNext(); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should() .Be(TaskQueueTestFixture.SemaphoreText + TaskQueueTestFixture.SemaphoreText); }
public void NullableIntFunc(int?num, string semaphoreFile) { TaskQueueTestFixture.WriteSemaphoreValue(semaphoreFile, num ?? -1); }
public void ItExecutesTasksOnlyOnceWhenUsingMultipleConsumers(string taskType) { var semaphoreFile = Path.GetTempFileName(); File.Delete(semaphoreFile); File.Create(semaphoreFile).Close(); var intervalSeconds = 5; var taskSchedulers = new[] { new TaskScheduler(TaskQueue.Redis(TaskQueueTestFixture.RedisConnectionString), restoreFromBackup: false), new TaskScheduler(TaskQueue.Redis(TaskQueueTestFixture.RedisConnectionString), restoreFromBackup: false), new TaskScheduler(TaskQueue.Redis(TaskQueueTestFixture.RedisConnectionString), restoreFromBackup: false), new TaskScheduler(TaskQueue.Redis(TaskQueueTestFixture.RedisConnectionString), restoreFromBackup: false) }; foreach (var taskScheduler in taskSchedulers) { if (taskType == "scheduled") { taskScheduler.AddScheduledTask(() => TaskQueueTestFixture.WriteSempaphore(semaphoreFile), TimeSpan.FromSeconds(intervalSeconds), RandomTaskName); } if (taskType == "recurring") { taskScheduler.AddRecurringTask(() => TaskQueueTestFixture.WriteSempaphore(semaphoreFile), TimeSpan.FromSeconds(intervalSeconds), RandomTaskName); } } Thread.Sleep(((intervalSeconds) * 1000) + 1000); // Ran only once Task.WaitAll( taskSchedulers.Select( taskScheduler => Task.Run(() => taskScheduler.Tick())).ToArray(), 1000); File.Exists(semaphoreFile).Should().Be(true); File.ReadAllText(semaphoreFile).Should().Be(TaskQueueTestFixture.SemaphoreText); // Ran only twice (or once if scheduled) Thread.Sleep(((intervalSeconds) * 1000) + 1000); Task.WaitAll( taskSchedulers.Select( taskScheduler => Task.Run(() => taskScheduler.Tick())).ToArray(), 1000); if (taskType == "recurring") { File.ReadAllText(semaphoreFile).Should() .Be(TaskQueueTestFixture.SemaphoreText + TaskQueueTestFixture.SemaphoreText); } if (taskType == "scheduled") { File.ReadAllText(semaphoreFile).Should() .Be(TaskQueueTestFixture.SemaphoreText); } taskSchedulers[0].FlushBackupStorage(); }