Beispiel #1
0
 public PutSubscriptionCommand(string databaseName, string query, string mentor, string uniqueRequestId) : base(databaseName, uniqueRequestId)
 {
     Query      = query;
     MentorNode = mentor;
     // this verifies that the query is a valid subscription query
     SubscriptionConnection.ParseSubscriptionQuery(query);
 }
        public SubscriptionConnectionState OpenSubscription(SubscriptionConnection connection)
        {
            var subscriptionState = _subscriptionConnectionStates.GetOrAdd(connection.SubscriptionId,
                                                                           subscriptionId => new SubscriptionConnectionState(subscriptionId, this));

            return(subscriptionState);
        }
Beispiel #3
0
        private async Task <bool> DispatchDatabaseTcpConnection(TcpConnectionOptions tcp, TcpConnectionHeaderMessage header)
        {
            var databaseLoadingTask = ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(header.DatabaseName);

            if (databaseLoadingTask == null)
            {
                DatabaseDoesNotExistException.Throw(header.DatabaseName);
                return(true);
            }

            var databaseLoadTimeout = ServerStore.DatabasesLandlord.DatabaseLoadTimeout;

            if (databaseLoadingTask.IsCompleted == false)
            {
                var resultingTask = await Task.WhenAny(databaseLoadingTask, Task.Delay(databaseLoadTimeout));

                if (resultingTask != databaseLoadingTask)
                {
                    ThrowTimeoutOnDatabaseLoad(header);
                }
            }

            tcp.DocumentDatabase = await databaseLoadingTask;
            if (tcp.DocumentDatabase == null)
            {
                DatabaseDoesNotExistException.Throw(header.DatabaseName);
            }

            Debug.Assert(tcp.DocumentDatabase != null);

            if (tcp.DocumentDatabase.DatabaseShutdown.IsCancellationRequested)
            {
                ThrowDatabaseShutdown(tcp.DocumentDatabase);
            }

            tcp.DocumentDatabase.RunningTcpConnections.Add(tcp);
            switch (header.Operation)
            {
            case TcpConnectionHeaderMessage.OperationTypes.Subscription:
                SubscriptionConnection.SendSubscriptionDocuments(tcp);
                break;

            case TcpConnectionHeaderMessage.OperationTypes.Replication:
                var documentReplicationLoader = tcp.DocumentDatabase.ReplicationLoader;
                documentReplicationLoader.AcceptIncomingConnection(tcp);
                break;

            default:
                throw new InvalidOperationException("Unknown operation for TCP " + header.Operation);
            }

            //since the responses to TCP connections mostly continue to run
            //beyond this point, no sense to dispose the connection now, so set it to null.
            //this way the responders are responsible to dispose the connection and the context
            // ReSharper disable once RedundantAssignment
            tcp = null;
            return(false);
        }
Beispiel #4
0
        private void OnEndConnection(SubscriptionConnection connection)
        {
            var subscriptionName = connection.SubscriptionState.SubscriptionName;

            if (_perSubscriptionConnectionStats.TryGetValue(subscriptionName, out var subscriptionAndStats))
            {
                var aggregatorToAdd = connection.GetPerformanceStats();
                subscriptionAndStats.Performance.TryAdd(aggregatorToAdd);
            }
        }
        private void SetSubscriptionConnectionStats(SubscriptionConnection connection, DynamicJsonValue config)
        {
            config["ClientUri"]           = connection.TcpConnection.TcpClient.Client.RemoteEndPoint.ToString();
            config["ConnectedAt"]         = connection.Stats.ConnectedAt;
            config["ConnectionException"] = connection.ConnectionException;

            config["LastMessageSentAt"] = connection.Stats.LastMessageSentAt;
            config["LastAckReceivedAt"] = connection.Stats.LastAckReceivedAt;

            config["DocsRate"]  = connection.Stats.DocsRate.CreateMeterData();
            config["BytesRate"] = connection.Stats.BytesRate.CreateMeterData();
            config["AckRate"]   = connection.Stats.AckRate.CreateMeterData();
        }
        public void RegisterRejectedConnection(SubscriptionConnection connection, SubscriptionException exception = null)
        {
            if (exception != null && connection.ConnectionException == null)
            {
                connection.ConnectionException = exception;
            }

            while (_rejectedConnections.Count > 10)
            {
                _rejectedConnections.TryDequeue(out SubscriptionConnection _);
            }
            _rejectedConnections.Enqueue(connection);
        }
Beispiel #7
0
        private async Task CreateInternal(BlittableJsonReaderObject bjro, SubscriptionCreationOptions options, DocumentsOperationContext context, long?id, bool?disabled)
        {
            if (TrafficWatchManager.HasRegisteredClients)
            {
                AddStringToHttpContext(bjro.ToString(), TrafficWatchChangeType.Subscriptions);
            }

            var sub = SubscriptionConnection.ParseSubscriptionQuery(options.Query);

            if (Enum.TryParse(options.ChangeVector, out Constants.Documents.SubscriptionChangeVectorSpecialStates changeVectorSpecialValue))
            {
                switch (changeVectorSpecialValue)
                {
                case Constants.Documents.SubscriptionChangeVectorSpecialStates.BeginningOfTime:

                    options.ChangeVector = null;
                    break;

                case Constants.Documents.SubscriptionChangeVectorSpecialStates.LastDocument:
                    options.ChangeVector = Database.DocumentsStorage.GetLastDocumentChangeVector(context.Transaction.InnerTransaction, context, sub.Collection);
                    break;
                }
            }

            var mentor         = options.MentorNode;
            var subscriptionId = await Database.SubscriptionStorage.PutSubscription(options, GetRaftRequestIdFromQuery(), id, disabled, mentor);

            var name = options.Name ?? subscriptionId.ToString();

            using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext serverContext))
                using (serverContext.OpenReadTransaction())
                {
                    // need to wait on the relevant remote node
                    var node = Database.SubscriptionStorage.GetResponsibleNode(serverContext, name);
                    if (node != null && node != ServerStore.NodeTag)
                    {
                        await WaitForExecutionOnSpecificNode(serverContext, ServerStore.GetClusterTopology(serverContext), node, subscriptionId);
                    }
                }

            HttpContext.Response.StatusCode = (int)HttpStatusCode.Created;

            await using (var writer = new AsyncBlittableJsonTextWriter(context, ResponseBodyStream()))
            {
                context.Write(writer, new DynamicJsonValue
                {
                    [nameof(CreateSubscriptionResult.Name)] = name
                });
            }
        }
        private static DynamicJsonValue GetSubscriptionConnectionDJV(SubscriptionConnection x)
        {
            if (x == null)
            {
                return(new DynamicJsonValue());
            }

            return(new DynamicJsonValue()
            {
                [nameof(SubscriptionConnection.ClientUri)] = x.ClientUri,
                [nameof(SubscriptionConnection.Strategy)] = x.Strategy,
                [nameof(SubscriptionConnection.Stats)] = GetConnectionStatsDJV(x.Stats),
                [nameof(SubscriptionConnection.ConnectionException)] = x.ConnectionException?.Message
            });
        }
        public async Task Create()
        {
            using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                using (context.OpenReadTransaction())
                {
                    var json = await context.ReadForMemoryAsync(RequestBodyStream(), null);

                    var options = JsonDeserializationServer.SubscriptionCreationParams(json);

                    if (TrafficWatchManager.HasRegisteredClients)
                    {
                        AddStringToHttpContext(json.ToString(), TrafficWatchChangeType.Subscriptions);
                    }

                    var sub = SubscriptionConnection.ParseSubscriptionQuery(options.Query);

                    if (Enum.TryParse(
                            options.ChangeVector,
                            out Constants.Documents.SubscriptionChangeVectorSpecialStates changeVectorSpecialValue))
                    {
                        switch (changeVectorSpecialValue)
                        {
                        case Constants.Documents.SubscriptionChangeVectorSpecialStates.BeginningOfTime:

                            options.ChangeVector = null;
                            break;

                        case Constants.Documents.SubscriptionChangeVectorSpecialStates.LastDocument:
                            options.ChangeVector = Database.DocumentsStorage.GetLastDocumentChangeVector(context, sub.Collection);
                            break;
                        }
                    }
                    var id             = GetLongQueryString("id", required: false);
                    var disabled       = GetBoolValueQueryString("disabled", required: false);
                    var mentor         = options.MentorNode;
                    var subscriptionId = await Database.SubscriptionStorage.PutSubscription(options, id, disabled, mentor : mentor);

                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; // Created

                    using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream()))
                    {
                        context.Write(writer, new DynamicJsonValue
                        {
                            ["Name"] = options.Name ?? subscriptionId.ToString()
                        });
                    }
                }
        }
Beispiel #10
0
        private static DynamicJsonValue GetSubscriptionConnectionJson(SubscriptionConnection x)
        {
            if (x == null)
            {
                return(new DynamicJsonValue());
            }

            return(new DynamicJsonValue()
            {
                [nameof(SubscriptionConnection.ClientUri)] = x.ClientUri,
                [nameof(SubscriptionConnection.Strategy)] = x.Strategy,
                [nameof(SubscriptionConnection.Stats)] = GetConnectionStatsJson(x.Stats),
                [nameof(SubscriptionConnection.ConnectionException)] = x.ConnectionException?.Message,
                ["TcpConnectionStats"] = x.TcpConnection.GetConnectionStats(),
                [nameof(SubscriptionConnection.RecentSubscriptionStatuses)] = new DynamicJsonArray(x.RecentSubscriptionStatuses?.ToArray() ?? Array.Empty <string>())
            });
        }
        public void HandleDatabaseRecordChange(DatabaseRecord databaseRecord)
        {
            using (_serverStore.ContextPool.AllocateOperationContext(out TransactionOperationContext context))
                using (context.OpenReadTransaction())
                {
                    foreach (var subscriptionStateKvp in _subscriptionConnectionStates)
                    {
                        var subscriptionName = subscriptionStateKvp.Value.Connection?.Options?.SubscriptionName;
                        if (subscriptionName == null)
                        {
                            continue;
                        }

                        var subscriptionBlittable = _serverStore.Cluster.Read(context, SubscriptionState.GenerateSubscriptionItemKeyName(databaseRecord.DatabaseName, subscriptionName));
                        if (subscriptionBlittable == null)
                        {
                            DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionDoesNotExistException($"The subscription {subscriptionName} had been deleted"));
                            continue;
                        }

                        var subscriptionState = JsonDeserializationClient.SubscriptionState(subscriptionBlittable);
                        if (subscriptionState.Disabled)
                        {
                            DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionClosedException($"The subscription {subscriptionName} is disabled and cannot be used until enabled"));
                            continue;
                        }

                        SubscriptionConnection connection = subscriptionStateKvp.Value.Connection;
                        if (connection != null && subscriptionState.Query != connection.SubscriptionState.Query)
                        {
                            DropSubscriptionConnection(subscriptionStateKvp.Key, new SubscriptionClosedException($"The subscription {subscriptionName} query has been modified, connection must be restarted"));
                            continue;
                        }

                        var whoseTaskIsIt = _db.WhoseTaskIsIt(databaseRecord.Topology, subscriptionState, subscriptionState);
                        if (whoseTaskIsIt != _serverStore.NodeTag)
                        {
                            DropSubscriptionConnection(subscriptionStateKvp.Key,
                                                       new SubscriptionDoesNotBelongToNodeException("Subscription operation was stopped, because it's now under different server's responsibility"));
                        }
                    }
                }
        }
Beispiel #12
0
        public bool TryGetRunningSubscriptionConnection(long subscriptionId, out SubscriptionConnection connection)
        {
            connection = null;

            if (_subscriptionConnectionStates.TryGetValue(subscriptionId, out var state) == false)
            {
                return(false);
            }

            var stateConnection = state.Connection;

            if (stateConnection == null)
            {
                return(false);
            }

            connection = stateConnection;

            return(true);
        }
 public DocumentsSubscriptionProcessor(ServerStore server, DocumentDatabase database, SubscriptionConnection connection) :
     base(server, database, connection)
 {
 }
Beispiel #14
0
        public async Task Try()
        {
            using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
            {
                var json = await context.ReadForMemoryAsync(RequestBodyStream(), null);

                var tryout = JsonDeserializationServer.SubscriptionTryout(json);

                var(collection, (script, functions), revisions) = SubscriptionConnection.ParseSubscriptionQuery(tryout.Query);
                SubscriptionPatchDocument patch = null;
                if (string.IsNullOrEmpty(script) == false)
                {
                    patch = new SubscriptionPatchDocument(script, functions);
                }

                if (collection == null)
                {
                    throw new ArgumentException("Collection must be specified");
                }

                var pageSize = GetIntValueQueryString("pageSize") ?? 1;

                var state = new SubscriptionState
                {
                    ChangeVectorForNextBatchStartingPoint = tryout.ChangeVector,
                    Query = tryout.Query
                };

                var fetcher = new SubscriptionDocumentsFetcher(Database, pageSize, -0x42,
                                                               new IPEndPoint(HttpContext.Connection.RemoteIpAddress, HttpContext.Connection.RemotePort), collection, revisions, state, patch);

                if (Enum.TryParse(
                        tryout.ChangeVector,
                        out Constants.Documents.SubscriptionChangeVectorSpecialStates changeVectorSpecialValue))
                {
                    switch (changeVectorSpecialValue)
                    {
                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.BeginningOfTime:
                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.DoNotChange:
                        state.ChangeVectorForNextBatchStartingPoint = null;
                        break;

                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.LastDocument:
                        state.ChangeVectorForNextBatchStartingPoint = Database.DocumentsStorage.GetLastDocumentChangeVector(context, collection);
                        break;
                    }
                }

                using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream()))
                {
                    writer.WriteStartObject();
                    writer.WritePropertyName("Results");
                    writer.WriteStartArray();

                    using (context.OpenReadTransaction())
                    {
                        var first = true;

                        foreach (var itemDetails in fetcher.GetDataToSend(context, 0))
                        {
                            if (itemDetails.Doc.Data == null)
                            {
                                continue;
                            }

                            if (first == false)
                            {
                                writer.WriteComma();
                            }

                            if (itemDetails.Exception == null)
                            {
                                writer.WriteDocument(context, itemDetails.Doc, metadataOnly: false);
                            }
                            else
                            {
                                var docWithExcepton = new DocumentWithException
                                {
                                    Exception    = itemDetails.Exception.ToString(),
                                    ChangeVector = itemDetails.Doc.ChangeVector,
                                    Id           = itemDetails.Doc.Id,
                                    DocumentData = itemDetails.Doc.Data
                                };
                                writer.WriteObject(context.ReadObject(docWithExcepton.ToJson(), ""));
                            }

                            first = false;
                        }
                    }

                    writer.WriteEndArray();
                    writer.WriteEndObject();
                }
            }
        }
        // we should have two locks: one lock for a connection and one lock for operations
        // remember to catch ArgumentOutOfRangeException for timeout problems
        public async Task <IDisposable> RegisterSubscriptionConnection(
            SubscriptionConnection incomingConnection,
            TimeSpan timeToWait)
        {
            try
            {
                if (await ConnectionInUse.WaitAsync(timeToWait) == false)
                {
                    switch (incomingConnection.Strategy)
                    {
                    // we try to connect, if the resource is occupied, we will throw an exception
                    // this piece of code could have been upper, but we choose to have it here, for better readability
                    case SubscriptionOpeningStrategy.WaitForFree:
                        throw new TimeoutException();

                    case SubscriptionOpeningStrategy.OpenIfFree:
                        throw new SubscriptionInUseException(
                                  $"Subscription {incomingConnection.SubscriptionId} is occupied, connection cannot be opened");

                    case SubscriptionOpeningStrategy.TakeOver:
                        if (_currentConnection?.Strategy == SubscriptionOpeningStrategy.TakeOver)
                        {
                            throw new SubscriptionInUseException(
                                      $"Subscription {incomingConnection.SubscriptionId} is already occupied by a TakeOver connection, connection cannot be opened");
                        }

                        if (_currentConnection != null)
                        {
                            _storage.DropSubscriptionConnection(_currentConnection.SubscriptionId,
                                                                new SubscriptionInUseException("Closed by TakeOver"));
                        }

                        throw new TimeoutException();

                    default:
                        throw new InvalidOperationException("Unknown subscription open strategy: " +
                                                            incomingConnection.Strategy);
                    }
                }
            }
            catch (SubscriptionException e)
            {
                RegisterRejectedConnection(incomingConnection, e);
                throw;
            }

            var subscriptionConnection = Interlocked.CompareExchange(ref _currentConnection, incomingConnection, null);

            if (subscriptionConnection != null && subscriptionConnection != incomingConnection)
            {
                throw new TimeoutException();
            }

            ConnectionInUse.Reset();

            return(new DisposableAction(() =>
            {
                while (_recentConnections.Count > 10)
                {
                    _recentConnections.TryDequeue(out SubscriptionConnection _);
                }
                _recentConnections.Enqueue(incomingConnection);
                Interlocked.CompareExchange(ref _currentConnection, null, incomingConnection);
                ConnectionInUse.Set();
            }));
        }
 public SubscriptionState(SubscriptionConnection currentConnection)
 {
     _currentConnection = currentConnection;
     _connectionInUse.Set();
 }
Beispiel #17
0
 public PutSubscriptionCommand(string databaseName, string query) : base(databaseName)
 {
     Query = query;
     // this verifies that the query is a valid subscription query
     SubscriptionConnection.ParseSubscriptionQuery(query);
 }
Beispiel #18
0
 protected SubscriptionProcessor(ServerStore server, DocumentDatabase database, SubscriptionConnection connection) :
     base(server, database, connection)
 {
     Logger = LoggingSource.Instance.GetLogger <SubscriptionProcessor <T> >(Database.Name);
 }
        // we should have two locks: one lock for a connection and one lock for operations
        // remember to catch ArgumentOutOfRangeException for timeout problems
        public async Task <IDisposable> RegisterSubscriptionConnection(
            SubscriptionConnection incomingConnection,
            int timeToWait)
        {
            try
            {
                if (await _connectionInUse.WaitAsync(TimeSpan.FromMilliseconds(timeToWait)) == false)
                {
                    switch (incomingConnection.Strategy)
                    {
                    // we try to connect, if the resource is occupied, we will throw an exception
                    // this piece of code could have been upper, but we choose to have it here, for better readability
                    case SubscriptionOpeningStrategy.WaitForFree:
                        throw new TimeoutException();

                    case SubscriptionOpeningStrategy.OpenIfFree:
                        throw new SubscriptionInUseException(
                                  $"Subscription {incomingConnection.SubscriptionId} is occupied, connection cannot be opened");

                    case SubscriptionOpeningStrategy.TakeOver:
                        if (_currentConnection?.Strategy == SubscriptionOpeningStrategy.ForceAndKeep)
                        {
                            throw  new SubscriptionInUseException(
                                      $"Subscription {incomingConnection.SubscriptionId} is occupied by a ForceAndKeep connection, connectionId cannot be opened");
                        }

                        if (_currentConnection != null)
                        {
                            _currentConnection.ConnectionException = new SubscriptionClosedException("Closed by Takeover");
                        }

                        _currentConnection?.CancellationTokenSource.Cancel();

                        throw new TimeoutException();

                    case SubscriptionOpeningStrategy.ForceAndKeep:
                        _currentConnection.ConnectionException = new SubscriptionClosedException("Closed by ForceAndKeep");
                        _currentConnection?.CancellationTokenSource.Cancel();

                        throw new TimeoutException();

                    default:
                        throw new InvalidOperationException("Unknown subscription open strategy: " +
                                                            incomingConnection.Strategy);
                    }
                }
            }
            catch (SubscriptionException e)
            {
                RegisterRejectedConnection(incomingConnection, e);
                throw;
            }

            _connectionInUse.Reset();
            _currentConnection = incomingConnection;
            return(new DisposableAction(() => {
                while (_recentConnections.Count > 10)
                {
                    SubscriptionConnection options;
                    _recentConnections.TryDequeue(out options);
                }
                _recentConnections.Enqueue(incomingConnection);
                _connectionInUse.SetByAsyncCompletion();
                _currentConnection = null;
            }));
        }
        public async Task Create()
        {
            using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
                using (context.OpenReadTransaction())
                {
                    var json = await context.ReadForMemoryAsync(RequestBodyStream(), null);

                    var options = JsonDeserializationServer.SubscriptionCreationParams(json);

                    if (TrafficWatchManager.HasRegisteredClients)
                    {
                        AddStringToHttpContext(json.ToString(), TrafficWatchChangeType.Subscriptions);
                    }

                    var sub = SubscriptionConnection.ParseSubscriptionQuery(options.Query);

                    if (Enum.TryParse(
                            options.ChangeVector,
                            out Constants.Documents.SubscriptionChangeVectorSpecialStates changeVectorSpecialValue))
                    {
                        switch (changeVectorSpecialValue)
                        {
                        case Constants.Documents.SubscriptionChangeVectorSpecialStates.BeginningOfTime:

                            options.ChangeVector = null;
                            break;

                        case Constants.Documents.SubscriptionChangeVectorSpecialStates.LastDocument:
                            options.ChangeVector = Database.DocumentsStorage.GetLastDocumentChangeVector(context.Transaction.InnerTransaction, context, sub.Collection);
                            break;
                        }
                    }
                    var id             = GetLongQueryString("id", required: false);
                    var disabled       = GetBoolValueQueryString("disabled", required: false);
                    var mentor         = options.MentorNode;
                    var subscriptionId = await Database.SubscriptionStorage.PutSubscription(options, GetRaftRequestIdFromQuery(), id, disabled, mentor : mentor);

                    var name = options.Name ?? subscriptionId.ToString();

                    using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext serverContext))
                        using (serverContext.OpenReadTransaction())
                        {
                            // need to wait on the relevant remote node
                            var node = Database.SubscriptionStorage.GetResponsibleNode(serverContext, name);
                            if (node != null && node != ServerStore.NodeTag)
                            {
                                await WaitForExecutionOnSpecificNode(serverContext, ServerStore.GetClusterTopology(serverContext), node, subscriptionId);
                            }
                        }

                    HttpContext.Response.StatusCode = (int)HttpStatusCode.Created; // Created

                    using (var writer = new BlittableJsonTextWriter(context, ResponseBodyStream()))
                    {
                        context.Write(writer, new DynamicJsonValue
                        {
                            ["Name"] = name
                        });
                    }
                }
        }
Beispiel #21
0
        public async Task Try()
        {
            using (ContextPool.AllocateOperationContext(out DocumentsOperationContext context))
            {
                BlittableJsonReaderObject json = await context.ReadForMemoryAsync(RequestBodyStream(), null);

                SubscriptionTryout tryout = JsonDeserializationServer.SubscriptionTryout(json);

                SubscriptionConnection.ParsedSubscription sub = SubscriptionConnection.ParseSubscriptionQuery(tryout.Query);
                SubscriptionPatchDocument patch = null;
                if (string.IsNullOrEmpty(sub.Script) == false)
                {
                    patch = new SubscriptionPatchDocument(sub.Script, sub.Functions);
                }

                if (sub.Collection == null)
                {
                    throw new ArgumentException("Collection must be specified");
                }

                const int maxPageSize = 1024;
                var       pageSize    = GetIntValueQueryString("pageSize") ?? 1;
                if (pageSize > maxPageSize)
                {
                    throw new ArgumentException($"Cannot gather more than {maxPageSize} results during tryouts, but requested number was {pageSize}.");
                }

                var state = new SubscriptionState
                {
                    ChangeVectorForNextBatchStartingPoint = tryout.ChangeVector,
                    Query = tryout.Query
                };

                var fetcher = new SubscriptionDocumentsFetcher(Database, int.MaxValue, -0x42,
                                                               new IPEndPoint(HttpContext.Connection.RemoteIpAddress, HttpContext.Connection.RemotePort), sub.Collection, sub.Revisions, state, patch);

                var includeCmd = new IncludeDocumentsCommand(Database.DocumentsStorage, context, sub.Includes, isProjection: patch != null);

                if (Enum.TryParse(
                        tryout.ChangeVector,
                        out Constants.Documents.SubscriptionChangeVectorSpecialStates changeVectorSpecialValue))
                {
                    switch (changeVectorSpecialValue)
                    {
                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.BeginningOfTime:
                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.DoNotChange:
                        state.ChangeVectorForNextBatchStartingPoint = null;
                        break;

                    case Constants.Documents.SubscriptionChangeVectorSpecialStates.LastDocument:
                        using (context.OpenReadTransaction())
                        {
                            state.ChangeVectorForNextBatchStartingPoint = Database.DocumentsStorage.GetLastDocumentChangeVector(context.Transaction.InnerTransaction, context, sub.Collection);
                        }
                        break;
                    }
                }
                else
                {
                    state.ChangeVectorForNextBatchStartingPoint = tryout.ChangeVector;
                }

                var changeVector = state.ChangeVectorForNextBatchStartingPoint.ToChangeVector();
                var cv           = changeVector.FirstOrDefault(x => x.DbId == Database.DbBase64Id);

                var sp        = Stopwatch.StartNew();
                var timeLimit = TimeSpan.FromSeconds(GetIntValueQueryString("timeLimit", false) ?? 15);
                var startEtag = cv.Etag;

                await using (var writer = new AsyncBlittableJsonTextWriter(context, ResponseBodyStream()))
                    using (context.OpenReadTransaction())
                    {
                        writer.WriteStartObject();
                        writer.WritePropertyName("Results");
                        writer.WriteStartArray();
                        var numberOfDocs = 0;
                        while (numberOfDocs == 0 && sp.Elapsed < timeLimit)
                        {
                            var first    = true;
                            var lastEtag = startEtag;
                            foreach (var itemDetails in fetcher.GetDataToSend(context, includeCmd, startEtag))
                            {
                                if (itemDetails.Doc.Data != null)
                                {
                                    using (itemDetails.Doc.Data)
                                    {
                                        includeCmd.Gather(itemDetails.Doc);

                                        if (first == false)
                                        {
                                            writer.WriteComma();
                                        }

                                        if (itemDetails.Exception == null)
                                        {
                                            writer.WriteDocument(context, itemDetails.Doc, metadataOnly: false);
                                        }
                                        else
                                        {
                                            var documentWithException = new DocumentWithException
                                            {
                                                Exception    = itemDetails.Exception.ToString(),
                                                ChangeVector = itemDetails.Doc.ChangeVector,
                                                Id           = itemDetails.Doc.Id,
                                                DocumentData = itemDetails.Doc.Data
                                            };
                                            writer.WriteObject(context.ReadObject(documentWithException.ToJson(), ""));
                                        }

                                        first = false;

                                        if (++numberOfDocs >= pageSize)
                                        {
                                            break;
                                        }
                                    }
                                }

                                if (sp.Elapsed >= timeLimit)
                                {
                                    break;
                                }

                                lastEtag = itemDetails.Doc.Etag;
                            }

                            if (startEtag == lastEtag)
                            {
                                break;
                            }

                            startEtag = lastEtag;
                        }

                        writer.WriteEndArray();
                        writer.WriteComma();
                        writer.WritePropertyName("Includes");
                        var includes = new List <Document>();
                        includeCmd.Fill(includes);
                        await writer.WriteIncludesAsync(context, includes);

                        writer.WriteEndObject();
                    }
            }
        }
Beispiel #22
0
        private void ListenToNewTcpConnection(TcpListener listener)
        {
            Task.Run(async() =>
            {
                TcpClient tcpClient;
                try
                {
                    tcpClient = await listener.AcceptTcpClientAsync();
                }
                catch (ObjectDisposedException)
                {
                    // shutting down
                    return;
                }
                catch (Exception e)
                {
                    if (_tcpLogger.IsInfoEnabled)
                    {
                        _tcpLogger.Info("Failed to accept new tcp connection", e);
                    }
                    return;
                }
                ListenToNewTcpConnection(listener);
                TcpConnectionOptions tcp = null;
                try
                {
                    tcpClient.NoDelay           = true;
                    tcpClient.ReceiveBufferSize = 32 * 1024;
                    tcpClient.SendBufferSize    = 4096;
                    var stream = tcpClient.GetStream();
                    tcp        = new TcpConnectionOptions()
                    {
                        Stream    = stream,
                        TcpClient = tcpClient,
                        DisposeOnConnectionClose =
                        {
                            stream,
                            tcpClient
                        }
                    };
                    tcp.DisposeOnConnectionClose.Add(
                        _tcpContextPool.AllocateOperationContext(out tcp.Context)
                        );


                    tcp.MultiDocumentParser = tcp.Context.ParseMultiFrom(stream);

                    try
                    {
                        TcpConnectionHeaderMessage header;
                        using (var headerJson = await tcp.MultiDocumentParser.ParseToMemoryAsync())
                        {
                            header = JsonDeserializationClient.TcpConnectionHeaderMessage(headerJson);
                            if (_logger.IsInfoEnabled)
                            {
                                _logger.Info($"New {header.Operation} TCP connection to {header.DatabaseName} from {tcpClient.Client.RemoteEndPoint}");
                            }
                        }
                        tcp.Operation           = header.Operation;
                        var databaseLoadingTask = ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(header.DatabaseName);
                        if (databaseLoadingTask == null)
                        {
                            ThrowNoSuchDatabase(header);
                            return;// never hit
                        }

                        var databaseLoadTimeout = ServerStore.DatabasesLandlord.DatabaseLoadTimeout;

                        if (databaseLoadingTask.IsCompleted == false)
                        {
                            var resultingTask = await Task.WhenAny(databaseLoadingTask, Task.Delay(databaseLoadTimeout));
                            if (resultingTask != databaseLoadingTask)
                            {
                                ThrowTimeoutOnDatbaseLoad(header);
                            }
                        }

                        tcp.DocumentDatabase = await databaseLoadingTask;
                        tcp.DocumentDatabase.RunningTcpConnections.Add(tcp);

                        switch (header.Operation)
                        {
                        case TcpConnectionHeaderMessage.OperationTypes.BulkInsert:
                            BulkInsertConnection.Run(tcp);
                            break;

                        case TcpConnectionHeaderMessage.OperationTypes.Subscription:
                            SubscriptionConnection.SendSubscriptionDocuments(tcp);
                            break;

                        case TcpConnectionHeaderMessage.OperationTypes.Replication:
                            var documentReplicationLoader = tcp.DocumentDatabase.DocumentReplicationLoader;
                            documentReplicationLoader.AcceptIncomingConnection(tcp);
                            break;

                        default:
                            throw new InvalidOperationException("Unknown operation for tcp " + header.Operation);
                        }

                        tcp = null;
                    }
                    catch (Exception e)
                    {
                        if (_tcpLogger.IsInfoEnabled)
                        {
                            _tcpLogger.Info("Failed to process TCP connection run", e);
                        }
                        if (tcp != null)
                        {
                            using (var errorWriter = new BlittableJsonTextWriter(tcp.Context, tcp.Stream))
                            {
                                tcp.Context.Write(errorWriter, new DynamicJsonValue
                                {
                                    ["Type"]      = "Error",
                                    ["Exception"] = e.ToString()
                                });
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    if (_tcpLogger.IsInfoEnabled)
                    {
                        _tcpLogger.Info("Failure when processing tcp connection", e);
                    }
                }
                finally
                {
                    tcp?.Dispose();
                }
            });
        }