public static void SendSubscriptionDocuments(TcpConnectionOptions tcpConnectionOptions) { var remoteEndPoint = tcpConnectionOptions.TcpClient.Client.RemoteEndPoint; Task.Run(async() => { using (tcpConnectionOptions) using (var connection = new SubscriptionConnection(tcpConnectionOptions)) using (tcpConnectionOptions.ConnectionProcessingInProgress("Subscription")) { try { bool gotSemaphore; if ((gotSemaphore = tcpConnectionOptions.DocumentDatabase.SubscriptionStorage.TryEnterSemaphore()) == false) { throw new SubscriptionClosedException( $"Cannot open new subscription connection, max amount of concurrent connections reached ({tcpConnectionOptions.DocumentDatabase.Configuration.Subscriptions.MaxNumberOfConcurrentConnections})"); } try { await connection.InitAsync(); await connection.ProcessSubscriptionAsync(); } finally { if (gotSemaphore) { tcpConnectionOptions.DocumentDatabase.SubscriptionStorage.ReleaseSubscriptionsSemaphore(); } } } catch (Exception e) { if (connection._logger.IsInfoEnabled) { connection._logger.Info( $"Failed to process subscription {connection.SubscriptionId} / from client {remoteEndPoint}", e); } try { await ReportExceptionToClient(connection, connection.ConnectionException ?? e); } catch (Exception) { // ignored } } finally { if (connection._logger.IsInfoEnabled) { connection._logger.Info( $"Finished processing subscription {connection.SubscriptionId} / from client {remoteEndPoint}"); } } } }); }
private static async Task ReportExceptionToClient(SubscriptionConnection connection, Exception ex, int recursionDepth = 0) { if (recursionDepth == 2) { return; } try { if (ex is SubscriptionDoesNotExistException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.NotFound), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionClosedException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Closed), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionInvalidStateException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Invalid), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionInUseException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.InUse), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionDoesNotBelongToNodeException subscriptionDoesNotBelongException) { if (connection._logger.IsInfoEnabled) { connection._logger.Info("Subscription does not belong to current node", ex); } await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Redirect), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Data)] = new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.SubscriptionRedirectData.RedirectedTag)] = subscriptionDoesNotBelongException.AppropriateNode } }); } else if (ex is SubscriptionChangeVectorUpdateConcurrencyException subscriptionConcurrency) { if (connection._logger.IsInfoEnabled) { connection._logger.Info("Subscription change vector update concurrency error", ex); } await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.ConcurrencyReconnect), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is CommandExecutionException commandExecution && commandExecution.InnerException is SubscriptionException) { await ReportExceptionToClient(connection, commandExecution.InnerException, recursionDepth - 1); } else { if (connection._logger.IsInfoEnabled) { connection._logger.Info("Subscription error", ex); } await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.Error), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.None), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } }
private static async Task ReportExceptionToClient(SubscriptionConnection connection, Exception ex) { try { if (ex is SubscriptionDoesNotExistException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.NotFound), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionClosedException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Closed), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionInvalidStateException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Invalid), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionInUseException) { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.InUse), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } else if (ex is SubscriptionDoesNotBelongToNodeException subscriptionDoesNotBelongException) { if (connection._logger.IsInfoEnabled) { connection._logger.Info("Subscription does not belong to current node", ex); } await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.ConnectionStatus), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.Redirect), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Data)] = new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.SubscriptionRedirectData.RedirectedTag)] = subscriptionDoesNotBelongException.AppropriateNode } }); } else { await connection.WriteJsonAsync(new DynamicJsonValue { [nameof(SubscriptionConnectionServerMessage.Type)] = nameof(SubscriptionConnectionServerMessage.MessageType.Error), [nameof(SubscriptionConnectionServerMessage.Status)] = nameof(SubscriptionConnectionServerMessage.ConnectionStatus.None), [nameof(SubscriptionConnectionServerMessage.Message)] = ex.Message, [nameof(SubscriptionConnectionServerMessage.Exception)] = ex.ToString() }); } } catch { // ignored } }
public static void SendSubscriptionDocuments(TcpConnectionOptions tcpConnectionOptions) { Task.Run(async() => { var connection = new SubscriptionConnection(tcpConnectionOptions); tcpConnectionOptions.DisposeOnConnectionClose.Add(connection); try { if (await connection.InitAsync() == false) { return; } await connection.ProcessSubscriptionAysnc(); } catch (Exception e) { if (connection._logger.IsInfoEnabled) { connection._logger.Info($"Failed to process subscription {connection._options?.SubscriptionId} / from client {connection.TcpConnection.TcpClient.Client.RemoteEndPoint}", e); } try { if (connection.ConnectionException != null) { return; } using (var writer = new BlittableJsonTextWriter(tcpConnectionOptions.Context, tcpConnectionOptions.Stream)) { tcpConnectionOptions.Context.Write(writer, new DynamicJsonValue { ["Type"] = "Error", ["Exception"] = e.ToString() }); } } catch (Exception) { // ignored } } finally { if (connection._options != null && connection._logger.IsInfoEnabled) { connection._logger.Info($"Finished proccessing subscription {connection._options?.SubscriptionId} / from client {connection.TcpConnection.TcpClient.Client.RemoteEndPoint}"); } if (connection.ConnectionException != null) { try { var status = "None"; if (connection.ConnectionException is SubscriptionClosedException) { status = "Closed"; } using (var writer = new BlittableJsonTextWriter(tcpConnectionOptions.Context, tcpConnectionOptions.Stream)) { tcpConnectionOptions.Context.Write(writer, new DynamicJsonValue { ["Type"] = "Error", ["Status"] = status, ["Exception"] = connection.ConnectionException.ToString() }); } } catch { // ignored } } tcpConnectionOptions.Dispose(); } }); }