Ejemplo n.º 1
0
        private async Task ProcessSubscriptionAysnc()
        {
            if (_logger.IsInfoEnabled)
            {
                _logger.Info($"Starting proccessing documents for subscription {SubscriptionId} received from {TcpConnection.TcpClient.Client.RemoteEndPoint}");
            }

            DocumentsOperationContext dbContext;

            using (DisposeOnDisconnect)
                using (TcpConnection.DocumentDatabase.DocumentsStorage.ContextPool.AllocateOperationContext(out dbContext))
                {
                    long startEtag;
                    SubscriptionCriteria criteria;
                    TcpConnection.DocumentDatabase.SubscriptionStorage.GetCriteriaAndEtag(_options.SubscriptionId, dbContext,
                                                                                          out criteria, out startEtag);

                    var replyFromClientTask = TcpConnection.MultiDocumentParser.ParseToMemoryAsync("client reply");
                    using (RegisterForNotificationOnNewDocuments(criteria))
                    {
                        var patch = SetupFilterScript(criteria);

                        while (CancellationTokenSource.IsCancellationRequested == false)
                        {
                            dbContext.ResetAndRenew();

                            bool anyDocumentsSentInCurrentIteration = false;
                            using (dbContext.OpenReadTransaction())
                            {
                                var documents = TcpConnection.DocumentDatabase.DocumentsStorage.GetDocumentsFrom(dbContext,
                                                                                                                 criteria.Collection,
                                                                                                                 startEtag + 1, 0, _options.MaxDocsPerBatch);
                                _buffer.SetLength(0);
                                var docsToFlush = 0;

                                var sendingCurrentBatchStopwatch = Stopwatch.StartNew();
                                foreach (var doc in documents)
                                {
                                    anyDocumentsSentInCurrentIteration = true;
                                    startEtag = doc.Etag;
                                    if (DocumentMatchCriteriaScript(patch, dbContext, doc) == false)
                                    {
                                        // make sure that if we read a lot of irrelevant documents, we send keep alive over the network
                                        if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                                        {
                                            await SendHeartBeat();

                                            sendingCurrentBatchStopwatch.Reset();
                                        }
                                        continue;
                                    }
                                    doc.EnsureMetadata();

                                    TcpConnection.Context.Write(_bufferedWriter, new DynamicJsonValue
                                    {
                                        ["Type"] = "Data",
                                        ["Data"] = doc.Data
                                    });
                                    docsToFlush++;

                                    // perform flush for current batch after 1000ms of running
                                    if (sendingCurrentBatchStopwatch.ElapsedMilliseconds > 1000)
                                    {
                                        if (docsToFlush > 0)
                                        {
                                            await FlushDocsToClient(docsToFlush);

                                            docsToFlush = 0;
                                            sendingCurrentBatchStopwatch.Reset();
                                        }
                                        else
                                        {
                                            await SendHeartBeat();
                                        }
                                    }
                                }

                                if (anyDocumentsSentInCurrentIteration)
                                {
                                    TcpConnection.Context.Write(_bufferedWriter, new DynamicJsonValue
                                    {
                                        ["Type"] = "EndOfBatch"
                                    });

                                    await FlushDocsToClient(docsToFlush, true);
                                }

                                if (anyDocumentsSentInCurrentIteration == false)
                                {
                                    if (await WaitForChangedDocuments(replyFromClientTask))
                                    {
                                        continue;
                                    }
                                }

                                SubscriptionConnectionClientMessage clientReply;

                                while (true)
                                {
                                    var result =
                                        await Task.WhenAny(replyFromClientTask, Task.Delay(TimeSpan.FromSeconds(5), CancellationTokenSource.Token));

                                    if (result == replyFromClientTask)
                                    {
                                        using (var reply = await replyFromClientTask)
                                        {
                                            TcpConnection.RegisterBytesReceived(reply.Size);
                                            clientReply = JsonDeserializationServer.SubscriptionConnectionClientMessage(reply);
                                        }

                                        TcpConnection.ResetAndRenew();

                                        replyFromClientTask = TcpConnection.MultiDocumentParser.ParseToMemoryAsync("client reply");
                                        break;
                                    }
                                    await SendHeartBeat();
                                }
                                switch (clientReply.Type)
                                {
                                case SubscriptionConnectionClientMessage.MessageType.Acknowledge:
                                    TcpConnection.DocumentDatabase.SubscriptionStorage.AcknowledgeBatchProcessed(_options.SubscriptionId,
                                                                                                                 clientReply.Etag);
                                    Stats.LastAckReceivedAt = DateTime.UtcNow;
                                    Stats.AckRate.Mark();
                                    await WriteJsonAsync(new DynamicJsonValue
                                    {
                                        ["Type"] = "Confirm",
                                        ["Etag"] = clientReply.Etag
                                    });

                                    break;

                                default:
                                    throw new ArgumentException("Unknown message type from client " +
                                                                clientReply.Type);
                                }
                            }
                        }
                    }
                }
        }