public async Task Test_EnqueueDoesNotChangePeekResult_SingleConsumer() { int futureTicks = 1; IQueuedTask peekTask = null, rePeekTask = null; string taskType = typeof(SampleTaskPayload) .FullName; PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => mDataSource .LastPostedAt .AddTicks(1)); PostgreSqlTaskQueueInfo taskQueueInfo = CreateTaskQueueInfo(() => mDataSource .LastPostedAt .AddTicks(futureTicks++)); peekTask = await taskQueueInfo.PeekAsync(); Assert.NotNull(peekTask); await taskQueueProducer.EnqueueAsync(payload : new SampleTaskPayload(100), source : nameof(Test_EnqueueDoesNotChangePeekResult_SingleConsumer), priority : 0); rePeekTask = await taskQueueInfo.PeekAsync(); Assert.NotNull(rePeekTask); //Placing a new element in a queue occurs at its end, // so peeking must not be affected // if no other operation occurs Assert.AreEqual(peekTask.Id, rePeekTask.Id); }
public async Task Test_CanEnqueue_NewTask_Serial() { Faker faker = new Faker(); ManualResetEvent notificationWaitHandle = new ManualResetEvent(false); DateTimeOffset postedAt = mDataSource.LastPostedAt .AddTicks(1); PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => postedAt); EventHandler <ClearForDequeueEventArgs> handleClearForDequeue = (s, e) => { if (e.Reason == ClearForDequeReason.NewTaskPostedNotificationReceived) { notificationWaitHandle.Set(); } }; using (PostgreSqlTaskQueueConsumer taskQueueConsumer = CreateTaskQueueConsumer(() => postedAt)) { taskQueueConsumer.ClearForDequeue += handleClearForDequeue; await taskQueueConsumer .StartReceivingNewTaskUpdatesAsync(); Assert.IsTrue(taskQueueConsumer .IsReceivingNewTaskUpdates); //Enqueue task and check result IQueuedTask queuedTask = await taskQueueProducer .EnqueueAsync(payload : new SampleTaskPayload(100), source : nameof(Test_CanEnqueue_NewTask_Serial), priority : faker.Random.Int(1, 100)); Assert.NotNull(queuedTask); await Assert_ResultAddedOrUpdatedCorrectly(queuedTask); notificationWaitHandle.WaitOne(); await taskQueueConsumer .StopReceivingNewTaskUpdatesAsync(); Assert.IsFalse(taskQueueConsumer .IsReceivingNewTaskUpdates); taskQueueConsumer.ClearForDequeue -= handleClearForDequeue; } }
public async Task Test_StatsAreUpdatedCorrectly_AfterEnqueue_RepostExistingTask() { Faker faker = new Faker(); DateTimeOffset postedAt = mDataSource.LastPostedAt .AddTicks(1); PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => postedAt); PostgreSqlTaskQueueInfo taskQueueInfo = CreateTaskQueueInfo(() => postedAt); foreach (IQueuedTaskToken token in mDataSource.CanBeRepostedSeededTaskTokens) { using (TaskQueueMetricsDiffChecker diff = new TaskQueueMetricsDiffChecker(async() => await taskQueueInfo.ComputeMetricsAsync())) { QueuedTaskStatus prevStatus = token .LastQueuedTaskResult .Status; await diff.CaptureInitialMetricsAsync(); QueuedTaskInfo repostTaskInfo = new QueuedTaskInfo() { Id = token.DequeuedTask.Id, Priority = faker.Random.Int(1, 100), Payload = token.DequeuedTask.Payload, Source = nameof(Test_StatsAreUpdatedCorrectly_AfterEnqueue_RepostExistingTask), Type = token.DequeuedTask.Type, LockedUntilTs = postedAt.AddMinutes(faker.Random.Long(1000, 10000)) }; //Remove task record from DB - only dequeued tasks get reposted await mDataSource.RemoveQueuedTaskFromDbByIdAsync(token .DequeuedTask .Id); await taskQueueProducer.EnqueueAsync(repostTaskInfo); await diff.CaptureNewMetricsAndAssertCorrectDiff(delta : new TaskQueueMetrics( totalUnprocessed: prevStatus != QueuedTaskStatus.Unprocessed ? 1 : 0, totalProcessing: prevStatus == QueuedTaskStatus.Processing ? -1 : 0, totalErrored: prevStatus == QueuedTaskStatus.Error ? -1 : 0, totalFaulted: prevStatus == QueuedTaskStatus.Faulted ? -1 : 0, totalFataled: prevStatus == QueuedTaskStatus.Fatal ? -1 : 0, totalProcessed: prevStatus == QueuedTaskStatus.Processed ? -1 : 0)); } } }
public StandardTaskEngine(TaskEngineOptions engineOptions, TaskQueueOptions producerAndResultOptions, TaskQueueConsumerOptions consumerOptions, ITaskExecutorRegistry executorRegistry, IExecutionPerformanceMonitorWriter executionPerfMonWriter, ITimestampProvider timestampProvider) { if (engineOptions == null) { throw new ArgumentNullException(nameof(engineOptions)); } if (consumerOptions == null) { throw new ArgumentNullException(nameof(consumerOptions)); } if (producerAndResultOptions == null) { throw new ArgumentNullException(nameof(producerAndResultOptions)); } mExecutorRegistry = executorRegistry ?? throw new ArgumentNullException(nameof(executorRegistry)); mExecutionPerfMonWriter = executionPerfMonWriter ?? throw new ArgumentNullException(nameof(executionPerfMonWriter)); mExecutionPerfMon = new StandardExecutionPerformanceMonitor(); mTaskQueueConsumer = new PostgreSqlTaskQueueConsumer(consumerOptions, timestampProvider); mTaskQueueProducer = new PostgreSqlTaskQueueProducer(producerAndResultOptions, timestampProvider); mTaskResultQueue = new PostgreSqlTaskResultQueue(producerAndResultOptions); mTaskBuffer = new StandardTaskBuffer(engineOptions.WorkerCount); mTaskPoller = new StandardTaskPoller(engineOptions.TaskProcessingOptions, mTaskQueueConsumer, mTaskBuffer); mOptions = engineOptions; }
public ITaskEngine BuildTaskEngine() { ITaskExecutorRegistry executorRegistry = mTaskExecutorRegistrySetup .BuildTaskExecutorRegistry(); IExecutionPerformanceMonitorWriter executionPerfMonWriter = mPerformanceMonitorWriterSetup .BuildWriter(); ITimestampProvider timestampProvider = new UtcNowTimestampProvider(); TaskQueueConsumerOptions consumerOptions = mTaskQueueConsumerSetup .BuildOptions(); TaskQueueOptions producerOptions = mTaskQueueProducerSetup .BuildOptions(); StakhanoviseLogManager.Provider = mLoggingProvider ?? new NoOpLoggingProvider(); ITaskQueueProducer taskQueueProducer = new PostgreSqlTaskQueueProducer(producerOptions, timestampProvider); ITaskQueueInfo taskQueueInfo = new PostgreSqlTaskQueueInfo(mTaskQueueInfoSetup.BuildOptions(), timestampProvider); if (mRegisterOwnDependencies) { executorRegistry.LoadDependencies(new Dictionary <Type, object>() { { typeof(ITaskQueueProducer), taskQueueProducer }, { typeof(ITaskQueueInfo), taskQueueInfo } }); } return(mTaskEngineSetup.BuildTaskEngine(consumerOptions, producerOptions, executorRegistry, executionPerfMonWriter, timestampProvider)); }
public async Task Test_StatsAreUpdatedCorrectly_AfterEnqueue_NewTask() { Faker faker = new Faker(); DateTimeOffset postedAt = mDataSource.LastPostedAt .AddTicks(1); PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => postedAt); PostgreSqlTaskQueueInfo taskQueueInfo = CreateTaskQueueInfo(() => postedAt); using (PostgreSqlTaskQueueConsumer taskQueueConsumer = CreateTaskQueueConsumer(() => postedAt)) using (TaskQueueMetricsDiffChecker diff = new TaskQueueMetricsDiffChecker(async() => await taskQueueInfo.ComputeMetricsAsync())) { await taskQueueConsumer.StartReceivingNewTaskUpdatesAsync(); Assert.IsTrue(taskQueueConsumer.IsReceivingNewTaskUpdates); //Capture previous metrics await diff.CaptureInitialMetricsAsync(); await taskQueueProducer.EnqueueAsync(payload : new SampleTaskPayload(100), source : nameof(Test_StatsAreUpdatedCorrectly_AfterEnqueue_NewTask), priority : faker.Random.Int(1, 100)); //Check that new metrics differ from the previous ones as expected await diff.CaptureNewMetricsAndAssertCorrectDiff(delta : new TaskQueueMetrics( totalUnprocessed: 1, totalProcessing: 0, totalErrored: 0, totalFaulted: 0, totalFataled: 0, totalProcessed: 0)); } }
public async Task Test_CanEnqueue_RepostExistingTask_Serial() { Faker faker = new Faker(); DateTimeOffset postedAt = mDataSource.LastPostedAt .AddSeconds(1); PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => postedAt); foreach (IQueuedTaskToken token in mDataSource.CanBeRepostedSeededTaskTokens) { QueuedTaskInfo repostTaskInfo = new QueuedTaskInfo() { Id = token.DequeuedTask.Id, Priority = faker.Random.Int(1, 100), Payload = token.DequeuedTask.Payload, Source = nameof(Test_CanEnqueue_RepostExistingTask_Serial), Type = token.DequeuedTask.Type, LockedUntilTs = postedAt.AddMilliseconds(faker.Random.Long(1000, 10000)) }; //Remove task record from DB - only dequeued tasks get reposted await mDataSource.RemoveQueuedTaskFromDbByIdAsync(token .DequeuedTask .Id); //Enqueue task and check result IQueuedTask requeuedTask = await taskQueueProducer .EnqueueAsync(repostTaskInfo); Assert.NotNull(requeuedTask); await Assert_ResultAddedOrUpdatedCorrectly(requeuedTask); } }
public async Task Test_CanEnqueue_NewTask_ParallelProducers(int nProducers) { Faker faker = new Faker(); CountdownEvent notificationWaitHandle = new CountdownEvent(nProducers); DateTimeOffset postedAt = mDataSource.LastPostedAt .AddTicks(1); PostgreSqlTaskQueueProducer taskQueueProducer = CreateTaskQueueProducer(() => postedAt); EventHandler <ClearForDequeueEventArgs> handleClearForDequeue = (s, e) => { if (e.Reason == ClearForDequeReason.NewTaskPostedNotificationReceived) { notificationWaitHandle.Signal(); } }; Task[] producers = new Task[nProducers]; using (PostgreSqlTaskQueueConsumer taskQueueConsumer = CreateTaskQueueConsumer(() => postedAt)) { taskQueueConsumer.ClearForDequeue += handleClearForDequeue; await taskQueueConsumer .StartReceivingNewTaskUpdatesAsync(); Assert.IsTrue(taskQueueConsumer .IsReceivingNewTaskUpdates); for (int i = 0; i < nProducers; i++) { producers[i] = Task.Run(async() => { //Enqueue task and check result IQueuedTask queuedTask = await taskQueueProducer .EnqueueAsync(payload: new SampleTaskPayload(100), source: nameof(Test_CanEnqueue_NewTask_ParallelProducers), priority: faker.Random.Int(1, 100)); Assert.NotNull(queuedTask); await Assert_ResultAddedOrUpdatedCorrectly(queuedTask); }); } notificationWaitHandle.Wait(); await taskQueueConsumer .StopReceivingNewTaskUpdatesAsync(); Assert.IsFalse(taskQueueConsumer .IsReceivingNewTaskUpdates); taskQueueConsumer.ClearForDequeue -= handleClearForDequeue; } }