예제 #1
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);
        }
예제 #2
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();
                }
            });
        }