public async Task Test_PeekMatchesDequeuedItem_SingleConsumer()
        {
            IQueuedTask      peekTask          = null;
            IQueuedTaskToken dequeuedTaskToken = null;

            PostgreSqlTaskQueueInfo taskQueueInfo =
                CreateTaskQueueInfo(() => mDataSource.LastPostedAt);

            using (PostgreSqlTaskQueueConsumer taskQueue =
                       CreateTaskQueueConsumer(() => mDataSource.LastPostedAt))
            {
                int expectedDequeueCount = mDataSource
                                           .NumTasksInQueue;

                for (int i = 0; i < expectedDequeueCount; i++)
                {
                    peekTask = await taskQueueInfo.PeekAsync();

                    Assert.NotNull(peekTask);

                    dequeuedTaskToken = await taskQueue.DequeueAsync();

                    Assert.NotNull(dequeuedTaskToken);

                    Assert.AreEqual(peekTask.Id, dequeuedTaskToken
                                    .DequeuedTask
                                    .Id);
                }
            }
        }
コード例 #2
0
		private ITaskExecutor ResolveTaskExecutor ( IQueuedTask queuedTask )
		{
			Type payloadType;
			ITaskExecutor taskExecutor = null;

			if ( ( payloadType = DetectTaskPayloadType( queuedTask ) ) != null )
			{
				mLogger.DebugFormat( "Runtime payload type {0} found for task type {1}.",
					payloadType,
					queuedTask.Type );

				taskExecutor = mExecutorRegistry
					.ResolveExecutor( payloadType );

				if ( taskExecutor != null )
					mLogger.DebugFormat( "Executor {0} found for task type {1}.",
						taskExecutor.GetType().FullName,
						queuedTask.Type );
				else
					mLogger.WarnFormat( "Executor not found for task type {0}.",
						queuedTask.Type );

			}
			else
				mLogger.WarnFormat( "Runtime payload type not found for task type {0}.",
					queuedTask.Type );

			return taskExecutor;
		}
        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_DequeueChangesPeekResult_SingleConsumer()
        {
            IQueuedTask peekTask   = null,
                        rePeekTask = null;

            IQueuedTaskToken dequeuedTaskToken = null;

            PostgreSqlTaskQueueInfo taskQueueInfo =
                CreateTaskQueueInfo(() => mDataSource.LastPostedAt);

            using (PostgreSqlTaskQueueConsumer taskQueueConsumer =
                       CreateTaskQueueConsumer(() => mDataSource.LastPostedAt))
            {
                peekTask = await taskQueueInfo.PeekAsync();

                Assert.NotNull(peekTask);

                dequeuedTaskToken = await taskQueueConsumer.DequeueAsync();

                Assert.NotNull(dequeuedTaskToken);

                rePeekTask = await taskQueueInfo.PeekAsync();

                Assert.NotNull(rePeekTask);

                //Removing a new element from the queue
                //  occurs at the beginning of the queue,
                //  so peeking must yield a different result
                //  than before dequeue-ing
                Assert.AreNotEqual(rePeekTask.Id,
                                   peekTask.Id);
            }
        }
コード例 #5
0
        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;
            }
        }
コード例 #6
0
        private void AssertCorrectDefaultIsTaskErrorRecoverableFn(StakhanoviseSetupDefaults setupDefaults)
        {
            IQueuedTask mockTask = MockQueuedTask();

            Assert.NotNull(setupDefaults.IsTaskErrorRecoverable);
            Assert.IsFalse(setupDefaults.IsTaskErrorRecoverable(mockTask, new NullReferenceException()));
            Assert.IsFalse(setupDefaults.IsTaskErrorRecoverable(mockTask, new ArgumentException()));

            Assert.IsTrue(setupDefaults.IsTaskErrorRecoverable(mockTask, new ApplicationException()));
            Assert.IsTrue(setupDefaults.IsTaskErrorRecoverable(mockTask, new Exception()));
            Assert.IsTrue(setupDefaults.IsTaskErrorRecoverable(mockTask, new NotSupportedException()));
        }
コード例 #7
0
        public QueuedTaskResult(IQueuedTask task)
        {
            if (task == null)
            {
                throw new ArgumentNullException(nameof(task));
            }

            Id         = task.Id;
            Type       = task.Type;
            Payload    = task.Payload;
            Status     = QueuedTaskStatus.Unprocessed;
            Source     = task.Source;
            Priority   = task.Priority;
            PostedAtTs = task.PostedAtTs;
            ProcessingTimeMilliseconds = 0;
        }
コード例 #8
0
        public async Task Test_CanPeek(int futureTicks)
        {
            IQueuedTask actualTopOfQueue;
            IQueuedTask expectedTopOfQueue = ExpectedTopOfQueueTask;

            for (int i = 0; i <= futureTicks; i++)
            {
                PostgreSqlTaskQueueInfo taskQueue = CreateTaskQueue(() => mDataSource
                                                                    .LastPostedAt
                                                                    .AddTicks(futureTicks));

                actualTopOfQueue = await taskQueue.PeekAsync();

                Assert.NotNull(actualTopOfQueue);
                Assert.AreEqual(expectedTopOfQueue.Id,
                                actualTopOfQueue.Id);
            }
        }
コード例 #9
0
        public async Task <IQueuedTask> PeekAsync()
        {
            IQueuedTask    peekedTask = null;
            DateTimeOffset refNow     = mTimestampProvider.GetNow();

            CheckNotDisposedOrThrow();

            //This simply returns the latest item on top of the queue,
            //  without acquiring any lock

            string peekSql = $@"SELECT q.*
				FROM {mOptions.Mapping.QueueTableName} as q
				WHERE q.task_locked_until_ts < @t_now 
				ORDER BY q.task_priority ASC,
					q.task_locked_until_ts ASC,
					q.task_lock_handle_id ASC
				LIMIT 1"                ;

            using (NpgsqlConnection conn = await OpenConnectionAsync())
                using (NpgsqlCommand peekCmd = new NpgsqlCommand(peekSql, conn))
                {
                    peekCmd.Parameters.AddWithValue("t_now",
                                                    NpgsqlDbType.TimestampTz,
                                                    refNow);

                    await peekCmd.PrepareAsync();

                    using (NpgsqlDataReader taskReader = await peekCmd.ExecuteReaderAsync())
                    {
                        if (await taskReader.ReadAsync())
                        {
                            peekedTask = await taskReader.ReadQueuedTaskAsync();
                        }

                        await taskReader.CloseAsync();
                    }

                    await conn.CloseAsync();
                }

            return(peekedTask);
        }
コード例 #10
0
        private async Task Assert_ResultAddedOrUpdatedCorrectly(IQueuedTask queuedTask)
        {
            IQueuedTaskResult queuedTaskResult = await mDataSource
                                                 .GetQueuedTaskResultFromDbByIdAsync(queuedTask.Id);

            Assert.NotNull(queuedTaskResult);
            Assert.NotNull(queuedTaskResult.Payload);

            Assert.AreEqual(queuedTask.Id,
                            queuedTaskResult.Id);
            Assert.AreEqual(queuedTask.Type,
                            queuedTaskResult.Type);
            Assert.AreEqual(queuedTask.Source,
                            queuedTaskResult.Source);
            Assert.AreEqual(queuedTask.Priority,
                            queuedTaskResult.Priority);
            Assert.LessOrEqual(Math.Abs((queuedTask.PostedAtTs - queuedTaskResult.PostedAtTs).TotalMilliseconds),
                               10);
            Assert.AreEqual(QueuedTaskStatus.Unprocessed,
                            queuedTaskResult.Status);
        }
コード例 #11
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);
            }
        }
コード例 #12
0
        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;
            }
        }
コード例 #13
0
 public void AddTask(IQueuedTask taskToAdd)
 {
     throw new NotImplementedException();
 }
コード例 #14
0
		private async Task<TaskExecutionResult> ExecuteTaskAsync ( TaskExecutionContext executionContext )
		{
			ITaskExecutor taskExecutor = null;
			DateTimeOffset retryAt = DateTimeOffset.UtcNow;

			IQueuedTask dequeuedTask = executionContext
				.TaskToken
				.DequeuedTask;

			try
			{
				//Check for cancellation before we start execution
				executionContext.StartTimingExecution();
				executionContext.ThrowIfCancellationRequested();

				//Attempt to resolve and run task executor
				if ( ( taskExecutor = ResolveTaskExecutor( dequeuedTask ) ) != null )
				{
					mLogger.DebugFormat( "Beginning task execution. Task id = {0}.",
						dequeuedTask.Id );

					//Execute task
					await taskExecutor.ExecuteAsync( dequeuedTask.Payload,
						executionContext );

					mLogger.DebugFormat( "Task execution completed. Task id = {0}.",
						dequeuedTask.Id );

					//Ensure we have a result - since no exception was thrown 
					//	and no result explicitly set, assume success.
					if ( !executionContext.HasResult )
						executionContext.NotifyTaskCompleted();
				}
			}
			catch ( OperationCanceledException )
			{
				//User code has observed cancellation request 
				executionContext?.NotifyCancellationObserved();
			}
			catch ( Exception exc )
			{
				mLogger.Error( "Error executing queued task",
					exception: exc );

				bool isRecoverable = mOptions.IsTaskErrorRecoverable( dequeuedTask,
					exc );

				executionContext?.NotifyTaskErrored( new QueuedTaskError( exc ),
					isRecoverable: isRecoverable );
			}
			finally
			{
				executionContext.StopTimingExecution();
			}

			//Compute the amount of time to delay task execution
			//	if execution failed
			if ( executionContext.HasResult
				&& executionContext.ExecutionFailed )
				retryAt = ComputeRetryAt( executionContext.TaskToken );

			return taskExecutor != null
				? new TaskExecutionResult( executionContext.ResultInfo,
					duration: executionContext.Duration,
					retryAt: retryAt,
					faultErrorThresholdCount: mOptions.FaultErrorThresholdCount )
				: null;
		}
コード例 #15
0
		private Type DetectTaskPayloadType ( IQueuedTask queuedTask )
		{
			return queuedTask.Payload != null
				? queuedTask.Payload.GetType()
				: mExecutorRegistry.ResolvePayloadType( queuedTask.Type );
		}
コード例 #16
0
 public void AddTask(IQueuedTask taskToAdd)
 {
     _logger.LogInformation("Adding task to background queue. {task}", taskToAdd);
     _tasks.Add(taskToAdd);
 }