示例#1
0
        async Task <ResourceResponse <Document> > SaveQueueItem(InternalCosmosDBQueueItem queueItem)
        {
            try
            {
                var uri        = UriFactory.CreateDocumentCollectionUri(this.settings.QueueCollectionDefinition.DbName, this.settings.QueueCollectionDefinition.CollectionName);
                var reqOptions = new RequestOptions()
                {
                    AccessCondition = new AccessCondition()
                    {
                        Condition = queueItem.etag,
                        Type      = AccessConditionType.IfMatch
                    }
                };

                var res = await queueCollectionClient.UpsertDocumentAsync(uri, queueItem, reqOptions);

                this.logger?.LogTrace($"Queue item {queueItem.id} updated, costs: {res.RequestCharge}, latency: {res.RequestLatency.TotalMilliseconds}ms");

                return(res);
            }
            catch (Exception ex)
            {
                this.logger?.LogError(ex, $"Error persisting queue item {queueItem.id}");
            }

            return(null);
        }
示例#2
0
        /// <summary>
        /// Tries to adquire lock in queue item to be the actual processor
        /// </summary>
        /// <remarks>
        /// Successfully adquiring a lock means:
        ///     - Document was updated with { status: 'InProgress', workerId: '[this_worker_id]', processStartTime: currentTime }, if the ETag did not change (optimistic locking)
        /// </remarks>
        /// <param name="documents"></param>
        /// <returns></returns>
        private async Task <IList <InternalCosmosDBQueueItem> > TryAdquireLock(IEnumerable <Document> documents)
        {
            // TODO: Replace with stored procedure to batch lock items
            var result = new List <InternalCosmosDBQueueItem>();

            foreach (var doc in documents)
            {
                try
                {
                    this.logger?.LogTrace($"Trying to lock item {doc.Id}");

                    var updatedDocument = new InternalCosmosDBQueueItem
                    {
                        status           = QueueItemStatus.InProgress,
                        data             = doc.GetPropertyValue <object>("data"),
                        id               = doc.Id,
                        processStartTime = doc.GetPropertyValue <long>(nameof(InternalCosmosDBQueueItem.processStartTime)),
                        queuedTime       = doc.GetPropertyValue <long>(nameof(InternalCosmosDBQueueItem.queuedTime)),
                        errors           = doc.GetPropertyValue <int>(nameof(InternalCosmosDBQueueItem.errors)),
                        completedTime    = doc.GetPropertyValue <long>(nameof(InternalCosmosDBQueueItem.completedTime)),
                    };

                    updatedDocument.SetWorker(this.workerId).SetWorkerExpiration(settings.ProcessingItemTimeout).SetProcessStartTime();


                    var res = await SaveQueueItem(updatedDocument);

                    this.logger?.LogTrace($"Queue item {updatedDocument.id} adquired lock succeeded, costs: {res.RequestCharge}, latency: {res.RequestLatency.TotalMilliseconds}ms");

                    var savedDocument = JsonConvert.DeserializeObject <InternalCosmosDBQueueItem>(res.Resource.ToString());
                    savedDocument.etag = res.ResponseHeaders["ETag"];
                    result.Add(savedDocument);
                }
                catch (DocumentClientException documentClientException)
                {
                    if (documentClientException.StatusCode != System.Net.HttpStatusCode.PreconditionFailed)
                    {
                        this.logger?.LogError(documentClientException, $"Error during queue item {doc.Id} adquired lock attempt");
                    }
                }
                catch (Exception ex)
                {
                    this.logger?.LogError(ex, $"Error during queue item {doc.Id} adquired lock attempt");
                }
            }

            return(result);
        }
示例#3
0
        /// <summary>
        /// Processes a single queue item
        /// </summary>
        /// <param name="queueItem"></param>
        /// <param name="persistDocument"></param>
        /// <returns></returns>
        async Task ProcessSingleItem(InternalCosmosDBQueueItem queueItem, bool persistDocument)
        {
            // Persist document only if Auto complete is enabled
            persistDocument = persistDocument & this.settings.AutoComplete;

            try
            {
                var externalQueueItem = new CosmosDBQueueItem(this, queueItem);
                var handlerStatus     = await OnMessage(externalQueueItem);

                if (handlerStatus)
                {
                    if (this.settings.AutoComplete)
                    {
                        queueItem.SetAsComplete();
                    }

                    this.logger?.LogTrace($"Processing item {queueItem.id} succeeded");
                }
                else
                {
                    if (this.settings.AutoComplete)
                    {
                        queueItem.SetAsPending();
                        queueItem.errors++;
                    }

                    this.logger?.LogTrace($"Processing item {queueItem.id} failed");
                }
            }
            catch (Exception ex)
            {
                this.logger?.LogError(ex, $"Error processing queue item {queueItem.id}");

                queueItem.errors++;
                queueItem.SetAsPending();

                // If the message handler failed we saved as failed
                persistDocument = true;
            }

            if (persistDocument)
            {
                await SaveQueueItem(queueItem);
            }
        }
示例#4
0
        /// <summary>
        /// Queue item returning queue item identifier
        /// </summary>
        /// <param name="payload"></param>
        /// <returns></returns>
        public async Task <string> Queue(string id, object payload)
        {
            if (!this.initialized)
            {
                throw new InvalidOperationException("Must call Initialize() first");
            }

            var queueItem = new InternalCosmosDBQueueItem()
            {
                id   = id,
                data = payload
            };

            var uri = UriFactory.CreateDocumentCollectionUri(this.settings.QueueCollectionDefinition.DbName, this.settings.QueueCollectionDefinition.CollectionName);

            var res = await this.queueCollectionClient.CreateDocumentAsync(uri, queueItem);

            this.logger?.LogTrace($"Item {res.Resource.Id} queued, costs: {res.RequestCharge}, latency: {res.RequestLatency.TotalMilliseconds}ms");

            return(res.Resource.Id);
        }
示例#5
0
 /// <summary>
 /// Completes the message queue
 /// </summary>
 /// <param name="queueItem"></param>
 /// <returns></returns>
 public async Task Abandon(InternalCosmosDBQueueItem queueItem)
 {
     queueItem.SetAsPending();
     queueItem.errors++;
     await SaveQueueItem(queueItem);
 }
示例#6
0
 /// <summary>
 /// Completes the message queue
 /// </summary>
 /// <param name="queueItem"></param>
 /// <returns></returns>
 public async Task Complete(InternalCosmosDBQueueItem queueItem)
 {
     queueItem.SetAsComplete();
     await SaveQueueItem(queueItem);
 }
示例#7
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="consumer"></param>
 /// <param name="queueItem"></param>
 protected internal CosmosDBQueueItem(CosmosDBQueueConsumer consumer, InternalCosmosDBQueueItem queueItem)
 {
     this.consumer  = consumer;
     this.queueItem = queueItem;
 }