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); }
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(); } }); }