protected void HandleExceptionForTransaction(T queueItem, IQueueTransaction queueTransaction, Action oldQueueItemAction = null)
        {
            if (queueTransaction == null)
            {
                throw new ArgumentNullException(nameof(queueTransaction), "The queue transaction is null.");
            }

            // The queue item is null, commit the transaction.
            if (queueItem == null)
            {
                oldQueueItemAction?.Invoke();
                queueTransaction.Commit();

                return;
            }

            try
            {
                var isMessageOld = DateTime.UtcNow - queueItem.AssociationDateTime > _dequeueServiceConfig.MaximumQueueMessageAge;

                // If the item is not null and we haven't dequeued too many times, add to the queue at the back
                if (queueItem.DequeueCount < DequeueServiceConfig.MaxDequeueCount && !isMessageOld)
                {
                    EnqueueMessage(queueItem, _dequeueQueuePath, queueTransaction);
                }
                // Enqueue onto the dead letter queue if the message isn't old
                else if (!isMessageOld)
                {
                    EnqueueMessage(queueItem, _deadLetterQueuePath, queueTransaction);

                    LogInformation(LogEntry.Create(MessageQueueStatus.EnqueuedDeadLetter,
                                                   queueItemBase: queueItem,
                                                   sourceMessageQueuePath: _deadLetterQueuePath));
                }
                // Remove from all queues if the dequeue count has been reached and the message is old. Invoke any clean-up
                // tasks in the action.
                else
                {
                    // Make sure this is called before commiting the transaction. This will most likely use the transaction
                    // to enqueue to the delete service queue.
                    oldQueueItemAction?.Invoke();

                    LogError(LogEntry.Create(MessageQueueStatus.TooOldForDeadLetterError,
                                             queueItemBase: queueItem,
                                             sourceMessageQueuePath: _dequeueQueuePath),
                             new Exception("Message dequeued too many times. Remove from all queues."));
                }

                queueTransaction.Commit();
            }
            catch (Exception e)
            {
                LogError(LogEntry.Create(MessageQueueStatus.TransactionExceptionHandlerError,
                                         queueItemBase: queueItem,
                                         sourceMessageQueuePath: _dequeueQueuePath),
                         e);

                queueTransaction.Abort();
            }
        }
        public T Receive <T>(IQueueTransaction transaction)
        {
            var trans = ((AdoNetTransaction)transaction).Transaction;
            AdoNetQueueEntry row;

            using (var cmd = trans.Connection.CreateCommand())
            {
                cmd.Transaction = trans;
                cmd.CommandText = @"SELECT TOP(1) * FROM Queue" + _queueName;
                row             = cmd.FirstOrDefault(_mapper);
            }

            if (row == null)
            {
                return(default(T));
            }

            using (var cmd = trans.Connection.CreateCommand())
            {
                cmd.Transaction = trans;
                cmd.CommandText = @"DELETE FROM Queue" + _queueName + " WHERE Id = @id";
                cmd.AddParameter("id", row.Id);
                cmd.ExecuteNonQuery();
            }
            return(JsonConvert.DeserializeObject <T>(row.Body));
        }
示例#3
0
        /// <summary>
        /// Uploads the files.
        /// </summary>
        /// <param name="dicomFiles">The dicom files.</param>
        /// <param name="uploadQueueItem">The upload queue item.</param>
        /// <param name="queueTransaction">The queue transaction.</param>
        /// <returns>The async task.</returns>
        private async Task ProcessUploadQueueItem(UploadQueueItem uploadQueueItem, IQueueTransaction queueTransaction)
        {
            var clientConfiguration = ApplyAETModelConfigProvider.GetAETConfigs(
                _aetConfigModels,
                uploadQueueItem.CalledApplicationEntityTitle,
                uploadQueueItem.CallingApplicationEntityTitle);

            LogTrace(LogEntry.Create(AssociationStatus.UploadProcessQueueItem));

            switch (clientConfiguration.Config.AETConfigType)
            {
            // ML Model or ML Model with Dry Run Result
            case AETConfigType.Model:
            case AETConfigType.ModelWithResultDryRun:
                // Load all DICOM files in the received folder.
                var dicomFiles = ReadDicomFiles(uploadQueueItem.AssociationFolderPath, uploadQueueItem);
                await ProcessModelConfig(dicomFiles, uploadQueueItem, queueTransaction, clientConfiguration).ConfigureAwait(false);

                break;

            // ML Model dry run
            case AETConfigType.ModelDryRun:
                // Anonymize and save the files locally for the dry run using the segmentation anonymisation protocol
                await AnonymiseAndSaveDicomFilesAsync(
                    anonymisationProtocolId : _innerEyeSegmentationClient.SegmentationAnonymisationProtocolId,
                    anonymisationProtocol : _innerEyeSegmentationClient.SegmentationAnonymisationProtocol,
                    uploadQueueItem : uploadQueueItem,
                    aETConfigType : clientConfiguration.Config.AETConfigType).ConfigureAwait(false);

                break;
            }
        }
示例#4
0
        /// <summary>
        /// Processes the model configuration.
        /// </summary>
        /// <param name="uploadQueueItem">The upload queue item.</param>
        /// <param name="dicomFiles">The dicom files.</param>
        /// <param name="queueTransaction">The queue transaction.</param>
        /// <param name="clientConfiguration">The client configuration.</param>
        /// <returns>The waitable task.</returns>
        private async Task ProcessModelConfig(
            IEnumerable <DicomFile> dicomFiles,
            UploadQueueItem uploadQueueItem,
            IQueueTransaction queueTransaction,
            ClientAETConfig clientConfiguration)
        {
            var modelMatchResult = ApplyAETModelConfigProvider.ApplyAETModelConfig(clientConfiguration.Config.ModelsConfig, dicomFiles);

            if (modelMatchResult.Matched)
            {
                var model     = modelMatchResult.Result;
                var queueItem = await StartSegmentationAsync(model.ChannelData, uploadQueueItem, model.ModelId, model.TagReplacements.ToArray(), clientConfiguration).ConfigureAwait(false);

                EnqueueMessage(queueItem, _downloadQueuePath, queueTransaction);
            }
            else
            {
                var failedDicomTags = modelMatchResult.GetDicomConstraintsDicomTags();

                // Log all the tags that did not match
                LogError(LogEntry.Create(AssociationStatus.UploadErrorTagsDoNotMatch,
                                         uploadQueueItem: uploadQueueItem,
                                         failedDicomTags: string.Join(",", failedDicomTags.Select(x => x.DictionaryEntry.Name))),
                         new ProcessorServiceException("Failed to find a model for the received Dicom data."));
            }
        }
        /// <summary>
        /// Begins the message queue transaction.
        /// MessageQueueTransaction.Begin() can throw exceptions. This method wraps the retry logic.
        /// </summary>
        /// <param name="queueTransaction">The queue transaction.</param>
        /// <param name="retrySeconds">The time delay between every retry.</param>
        /// <param name="maximumRetry">The maximum number of retries.</param>
        /// <exception cref="InvalidOperationException">If the transaction has already been started.</exception>
        /// <exception cref="MessageQueueTransactionBeginException">If we failed to start a transaction after retrying.</exception>
        protected void BeginMessageQueueTransaction(IQueueTransaction queueTransaction, int retrySeconds = 30, int maximumRetry = 120)
        {
            if (queueTransaction == null)
            {
                throw new ArgumentNullException(nameof(queueTransaction), "The queue transaction is null.");
            }

            try
            {
                queueTransaction.Begin();
                return;
            }
            catch (MessageQueueTransactionBeginException e)
            {
                LogError(LogEntry.Create(MessageQueueStatus.BeginTransactionError),
                         e);

                // Throw if we reach the retry limit or cancellation has been requested (service stop)
                if (maximumRetry <= 0 || _cancellationTokenSource.IsCancellationRequested)
                {
                    throw;
                }
            }

            // Delay before retrying
            _cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(retrySeconds));

            BeginMessageQueueTransaction(queueTransaction, retrySeconds, maximumRetry - 1);
        }
        /// <summary>
        /// Enqueues the message onto the message queue.
        /// Note: This method will not commit or abort the queue transaction.
        /// </summary>
        /// <typeparam name="TMessageType">The enqueue message type.</typeparam>
        /// <param name="message">The message to enqueue.</param>
        /// <param name="messageQueuePath">The queue path to enqueue the message onto.</param>
        /// <param name="queueTransaction">The queue transaction.</param>
        /// <exception cref="ArgumentException">If the queue path is not correct.</exception>
        /// <exception cref="MessageQueuePermissionsException">If we do not have permissions to write to the message queue.</exception>
        /// <exception cref="MessageQueueWriteException">If the message queue could not write to the queue.</exception>
        protected void EnqueueMessage <TMessageType>(
            TMessageType message,
            string messageQueuePath,
            IQueueTransaction queueTransaction) where TMessageType : QueueItemBase
        {
            try
            {
                GetMessageQueue(messageQueuePath).Enqueue(message, queueTransaction);

                LogInformation(LogEntry.Create(MessageQueueStatus.Enqueued,
                                               queueItemBase: message,
                                               sourceMessageQueuePath: messageQueuePath));
            }
            catch (MessageQueuePermissionsException e)
            {
                LogError(LogEntry.Create(MessageQueueStatus.EnqueueError,
                                         queueItemBase: message,
                                         sourceMessageQueuePath: messageQueuePath),
                         e);

                // We cannot recover from access violations. We should stop the service.
                StopServiceAsync();

                throw;
            }
        }
        /// <summary>
        /// The clean up action if the queue item has completed or failed and is now an old message.
        /// </summary>
        /// <param name="queueItem">The queue item.</param>
        /// <param name="transaction">The message queue transaction.</param>
        private void CleanUp(DownloadQueueItem queueItem, IQueueTransaction transaction)
        {
            if (queueItem == null || transaction == null || string.IsNullOrWhiteSpace(queueItem.ResultsDirectory))
            {
                return;
            }

            EnqueueMessage(
                new DeleteQueueItem(queueItem, queueItem.ResultsDirectory),
                _deleteQueuePath,
                transaction);
        }
示例#8
0
        /// <summary>
        /// The clean up action if the queue item has completed or failed and is now an old message.
        /// </summary>
        /// <param name="queueItem">The queue item.</param>
        /// <param name="transaction">The message queue transaction.</param>
        private void CleanUp(PushQueueItem queueItem, IQueueTransaction transaction)
        {
            if (queueItem == null || transaction == null)
            {
                return;
            }

            EnqueueMessage(
                new DeleteQueueItem(queueItem, queueItem.FilePaths),
                _deleteQueuePath,
                transaction);
        }
示例#9
0
        /// <summary>
        /// The clean up action if the queue item has completed or failed and is now an old message.
        /// </summary>
        /// <param name="queueItem">The queue item.</param>
        /// <param name="transaction">The message queue transaction.</param>
        private void CleanUp(UploadQueueItem queueItem, IQueueTransaction transaction)
        {
            if (queueItem == null || transaction == null)
            {
                return;
            }

            // During a clean-up with remove everything in the association folder.
            EnqueueMessage(
                new DeleteQueueItem(queueItem, queueItem.AssociationFolderPath),
                _deleteQueuePath,
                transaction);
        }
示例#10
0
 public T TryReceive <T>(IQueueTransaction transaction, TimeSpan waitTimeout)
 {
     try
     {
         var trans   = ((MsmqTransactionAdapter)transaction).Transaction;
         var message = _queue.Receive(waitTimeout, trans);
         return(Deserialize <T>(message));
     }
     catch (MessageQueueException ex)
     {
         if (ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
         {
             return(default(T));
         }
         throw;
     }
 }
示例#11
0
        protected static T TryDequeue <T>(IMessageQueue messageQueue, IQueueTransaction messageQueueTransaction, int timeoutMs = 2000)
        {
            var startTime = DateTime.UtcNow;

            while ((DateTime.UtcNow - startTime).TotalMilliseconds < timeoutMs)
            {
                try
                {
                    return(messageQueue.DequeueNextMessage <T>(messageQueueTransaction));
                }
                catch (Exception)
                {
                    Task.WaitAll(Task.Delay(500));
                }
            }

            throw new MessageQueueReadException("Failed to transactional dequeue.");
        }
        /// <summary>
        /// Dequeues the next message from the queue.
        /// Note: This method will not commit or abort the queue transaction.
        /// </summary>
        /// <param name="queueTransaction">The queue transaction.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The de-queued object.</returns>
        /// <exception cref="ArgumentException">If the queue path is not correct.</exception>
        /// <exception cref="MessageQueueReadException">The queue timed out when reading.</exception>
        /// <exception cref="MessageQueuePermissionsException">The queue does not have permissions to read.</exception>
        protected async Task <T> DequeueNextMessageAsync(
            IQueueTransaction queueTransaction,
            CancellationToken cancellationToken)
        {
            // Check if we should move all the dead letter messages on every dequeue.
            CheckMoveDeadLetterMessages();

            try
            {
                // Note: We do not put the message queue in a using block. The service manages the lifecycle
                // of message queues.
                var messageItem = GetMessageQueue(_dequeueQueuePath)
                                  .DequeueNextMessage <T>(queueTransaction);

                // Increase the count on the message.
                // Note: We should not check if the maximum dequeue count has been reached here. We should do that on handling exceptions.
                messageItem.DequeueCount++;

                LogInformation(LogEntry.Create(MessageQueueStatus.Dequeued,
                                               queueItemBase: messageItem,
                                               sourceMessageQueuePath: _dequeueQueuePath));

                return(messageItem);
            }
            catch (MessageQueueReadException)
            {
                // Delay here before throw the exception up the next level to delay message queue reads.
                await Task.Delay(DequeueServiceConfig.DequeueTimeout, cancellationToken);

                throw;
            }
            catch (MessageQueuePermissionsException e)
            {
                LogError(LogEntry.Create(MessageQueueStatus.DequeueError, sourceMessageQueuePath: _dequeueQueuePath),
                         e);

                // We cannot recover from access violations. We should stop the service.
                StopServiceAsync();

                throw;
            }
        }
 public T TryReceive <T>(IQueueTransaction transaction, TimeSpan waitTimeout)
 {
     return(Receive <T>());
 }