Exemplo n.º 1
0
        private void SelectDatabase(PhysicalConnection connection, Message message)
        {
            int db = message.Db;

            if (db >= 0)
            {
                var sel = connection.GetSelectDatabaseCommand(db, message);
                if (sel != null)
                {
                    connection.Enqueue(sel);
                    sel.WriteImpl(connection);
                    sel.SetRequestSent();
                    IncrementOpCount();
                }
            }
        }
Exemplo n.º 2
0
 internal void WriteTo(PhysicalConnection physical)
 {
     try
     {
         WriteImpl(physical);
     }
     catch (RedisCommandException)
     { // these have specific meaning; don't wrap
         throw;
     }
     catch (Exception ex)
     {
         physical?.OnInternalError(ex);
         Fail(ConnectionFailureType.InternalFailure, ex);
     }
 }
Exemplo n.º 3
0
 internal void WriteDirectOrQueueFireAndForget <T>(PhysicalConnection connection, Message message, ResultProcessor <T> processor)
 {
     if (message != null)
     {
         message.SetSource(processor, null);
         if (connection == null)
         {
             multiplexer.Trace("Enqueue: " + message);
             GetBridge(message.Command).TryEnqueue(message, isSlave);
         }
         else
         {
             multiplexer.Trace("Writing direct: " + message);
             connection.Bridge.WriteMessageDirect(connection, message);
         }
     }
 }
Exemplo n.º 4
0
 protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
 {
     switch (result.Type)
     {
     case ResultType.MultiBulk:
         var  arr = result.GetItems();
         long i64;
         if (arr.Length == 2 && arr[1].Type == ResultType.MultiBulk && arr[0].TryGetInt64(out i64))
         {
             var keysResult = new ScanResult(i64, arr[1].GetItemsAsKeys());
             SetResult(message, keysResult);
             return(true);
         }
         break;
     }
     return(false);
 }
Exemplo n.º 5
0
 internal void OnConnected(PhysicalConnection connection, TextWriter log)
 {
     Trace("OnConnected");
     if (physical == connection && !isDisposed && ChangeState(State.Connecting, State.ConnectedEstablishing))
     {
         ServerEndPoint.OnEstablishing(connection, log);
     }
     else
     {
         try
         {
             connection.Dispose();
         }
         catch
         { }
     }
 }
 private static Action <Task <int> > EndReadFactory(PhysicalConnection physical)
 {
     return(result =>
     {   // can't capture AsyncState on SocketRead, so we'll do it once per physical instead
         try
         {
             physical.Multiplexer.Trace("Completed asynchronously: processing in callback", physical.physicalName);
             if (physical.EndReading(result))
             {
                 physical.BeginReading();
             }
         }
         catch (Exception ex)
         {
             physical.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
         }
     });
 }
 public override bool SetResult(PhysicalConnection connection, Message message, RawResult result)
 {
     if (result.IsError)
     {
         var tran = message as TransactionMessage;
         if (tran != null)
         {
             string error  = result.GetString();
             var    bridge = connection.Bridge;
             foreach (var op in tran.InnerOperations)
             {
                 ServerFail(op.Wrapped, error);
                 bridge.CompleteSyncOrAsync(op.Wrapped);
             }
         }
     }
     return(base.SetResult(connection, message, result));
 }
Exemplo n.º 8
0
 internal void OnFullyEstablished(PhysicalConnection connection)
 {
     try
     {
         if (connection == null)
         {
             return;
         }
         var bridge = connection.Bridge;
         if (bridge == subscription)
         {
             multiplexer.ResendSubscriptions(this);
         }
         multiplexer.OnConnectionRestored(endpoint, bridge.ConnectionType);
     }
     catch (Exception ex)
     {
         connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
     }
 }
Exemplo n.º 9
0
 internal void OnFullyEstablished(PhysicalConnection connection)
 {
     Trace("OnFullyEstablished");
     if (physical == connection && !isDisposed && ChangeState(State.ConnectedEstablishing, State.ConnectedEstablished))
     {
         reportNextFailure = reconfigureNextFailure = true;
         LastException     = null;
         Interlocked.Exchange(ref failConnectCount, 0);
         ServerEndPoint.OnFullyEstablished(connection);
         Multiplexer.RequestWrite(this, true);
         if (ConnectionType == ConnectionType.Interactive)
         {
             ServerEndPoint.CheckInfoReplication();
         }
     }
     else
     {
         try { connection.Dispose(); } catch { }
     }
 }
Exemplo n.º 10
0
        // internally, this is very similar to RawResult, except it is designed to be usable
        // outside of the IO-processing pipeline: the buffers are standalone, etc

        internal static RedisResult TryCreate(PhysicalConnection connection, RawResult result)
        {
            try
            {
                switch (result.Type)
                {
                case ResultType.Integer:
                case ResultType.SimpleString:
                case ResultType.BulkString:
                    return(new SingleRedisResult(result.AsRedisValue()));

                case ResultType.MultiBulk:
                    var items = result.GetItems();
                    var arr   = new RedisResult[items.Length];
                    for (int i = 0; i < arr.Length; i++)
                    {
                        var next = TryCreate(connection, items[i]);
                        if (next == null)
                        {
                            return(null);                  // means we didn't understand
                        }
                        arr[i] = next;
                    }
                    return(new ArrayRedisResult(arr));

                case ResultType.Error:
                    return(new ErrorRedisResult(result.GetString()));

                default:
                    return(null);
                }
            } catch (Exception ex)
            {
                connection?.OnInternalError(ex);
                return(null); // will be logged as a protocol fail by the processor
            }
        }
Exemplo n.º 11
0
 internal override void WriteImpl(PhysicalConnection physical)
 {
     physical.WriteHeader(Command, 2);
     physical.Write(Channel);
     physical.Write(value);
 }
Exemplo n.º 12
0
        internal void RemovePhysical(PhysicalConnection connection)
        {
#pragma warning disable 0420
            Interlocked.CompareExchange(ref physical, null, connection);
#pragma warning restore 0420
        }
Exemplo n.º 13
0
 internal abstract void WriteImpl(PhysicalConnection physical);
Exemplo n.º 14
0
 internal void Fail(ConnectionFailureType failure, Exception innerException)
 {
     PhysicalConnection.IdentifyFailureType(innerException, ref failure);
     resultProcessor?.ConnectionFail(this, failure, innerException);
 }
Exemplo n.º 15
0
 // true if ready to be completed (i.e. false if re-issued to another server)
 internal bool ComputeResult(PhysicalConnection connection, RawResult result)
 {
     return(resultProcessor == null || resultProcessor.SetResult(connection, this, result));
 }
Exemplo n.º 16
0
        void Handshake(PhysicalConnection connection, TextWriter log)
        {
            multiplexer.LogLocked(log, "Server handshake");
            if (connection == null)
            {
                multiplexer.Trace("No connection!?");
                return;
            }
            Message msg;
            string  password = multiplexer.RawConfig.Password;

            if (!string.IsNullOrWhiteSpace(password))
            {
                multiplexer.LogLocked(log, "Authenticating (password)");
                msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.AUTH, (RedisValue)password);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
            }
            if (multiplexer.CommandMap.IsAvailable(RedisCommand.CLIENT))
            {
                string name = multiplexer.ClientName;
                if (!string.IsNullOrWhiteSpace(name))
                {
                    name = nameSanitizer.Replace(name, "");
                    if (!string.IsNullOrWhiteSpace(name))
                    {
                        multiplexer.LogLocked(log, "Setting client name: {0}", name);
                        msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, RedisLiterals.SETNAME, (RedisValue)name);
                        msg.SetInternalCall();
                        WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
                    }
                }
            }

            var connType = connection.Bridge.ConnectionType;

            if (connType == ConnectionType.Interactive)
            {
                multiplexer.LogLocked(log, "Auto-configure...");
                AutoConfigure(connection);
            }
            multiplexer.LogLocked(log, "Sending critical tracer: {0}", connection.Bridge);
            var tracer = GetTracerMessage(true);

            tracer = LoggingMessage.Create(log, tracer);
            WriteDirectOrQueueFireAndForget(connection, tracer, ResultProcessor.EstablishConnection);


            // note: this **must** be the last thing on the subscription handshake, because after this
            // we will be in subscriber mode: regular commands cannot be sent
            if (connType == ConnectionType.Subscription)
            {
                var configChannel = multiplexer.ConfigurationChangedChannel;
                if (configChannel != null)
                {
                    msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, (RedisChannel)configChannel);
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.TrackSubscriptions);
                }
            }
            multiplexer.LogLocked(log, "Flushing outbound buffer");
            connection.Flush();
        }
Exemplo n.º 17
0
 internal override void WriteImpl(PhysicalConnection physical)
 {
     wrapped.WriteImpl(physical);
     wrapped.SetRequestSent();
 }
Exemplo n.º 18
0
        internal void AutoConfigure(PhysicalConnection connection)
        {
            if (serverType == ServerType.Twemproxy)
            {
                // don't try to detect configuration; all the config commands are disabled, and
                // the fallback master/slave detection won't help
                return;
            }

            var commandMap           = multiplexer.CommandMap;
            const CommandFlags flags = CommandFlags.FireAndForget | CommandFlags.HighPriority | CommandFlags.NoRedirect;

            var     features = GetFeatures();
            Message msg;

            if (commandMap.IsAvailable(RedisCommand.CONFIG))
            {
                if (multiplexer.RawConfig.KeepAlive <= 0)
                {
                    msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.timeout);
                    msg.SetInternalCall();
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
                }
                msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.slave_read_only);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
                msg = Message.Create(-1, flags, RedisCommand.CONFIG, RedisLiterals.GET, RedisLiterals.databases);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
            }
            if (commandMap.IsAvailable(RedisCommand.INFO))
            {
                lastInfoReplicationCheckTicks = Environment.TickCount;
                if (features.InfoSections)
                {
                    msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.replication);
                    msg.SetInternalCall();
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);

                    msg = Message.Create(-1, flags, RedisCommand.INFO, RedisLiterals.server);
                    msg.SetInternalCall();
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
                }
                else
                {
                    msg = Message.Create(-1, flags, RedisCommand.INFO);
                    msg.SetInternalCall();
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
                }
            }
            else if (commandMap.IsAvailable(RedisCommand.SET))
            {
                // this is a nasty way to find if we are a slave, and it will only work on up-level servers, but...
                RedisKey key = Guid.NewGuid().ToByteArray();
                msg = Message.Create(0, flags, RedisCommand.SET, key, RedisLiterals.slave_read_only, RedisLiterals.PX, 1, RedisLiterals.NX);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.AutoConfigure);
            }
            if (commandMap.IsAvailable(RedisCommand.CLUSTER))
            {
                msg = Message.Create(-1, flags, RedisCommand.CLUSTER, RedisLiterals.NODES);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.ClusterNodes);
            }
        }
Exemplo n.º 19
0
            protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
            {
                var tran = message as TransactionMessage;

                if (tran != null)
                {
                    var bridge  = connection.Bridge;
                    var wrapped = tran.InnerOperations;
                    switch (result.Type)
                    {
                    case ResultType.SimpleString:
                        if (tran.IsAborted && result.IsEqual(RedisLiterals.BytesOK))
                        {
                            connection.Multiplexer.Trace("Acknowledging UNWATCH (aborted electively)");
                            SetResult(message, false);
                            return(true);
                        }
                        //EXEC returned with a NULL
                        if (!tran.IsAborted && result.IsNull)
                        {
                            connection.Multiplexer.Trace("Server aborted due to failed EXEC");
                            //cancel the commands in the transaction and mark them as complete with the completion manager
                            foreach (var op in wrapped)
                            {
                                op.Wrapped.Cancel();
                                bridge.CompleteSyncOrAsync(op.Wrapped);
                            }
                            SetResult(message, false);
                            return(true);
                        }
                        break;

                    case ResultType.MultiBulk:
                        if (!tran.IsAborted)
                        {
                            var arr = result.GetItems();
                            if (arr == null)
                            {
                                connection.Multiplexer.Trace("Server aborted due to failed WATCH");
                                foreach (var op in wrapped)
                                {
                                    op.Wrapped.Cancel();
                                    bridge.CompleteSyncOrAsync(op.Wrapped);
                                }
                                SetResult(message, false);
                                return(true);
                            }
                            else if (wrapped.Length == arr.Length)
                            {
                                connection.Multiplexer.Trace("Server committed; processing nested replies");
                                for (int i = 0; i < arr.Length; i++)
                                {
                                    if (wrapped[i].Wrapped.ComputeResult(connection, arr[i]))
                                    {
                                        bridge.CompleteSyncOrAsync(wrapped[i].Wrapped);
                                    }
                                }
                                SetResult(message, true);
                                return(true);
                            }
                        }
                        break;
                    }
                    // even if we didn't fully understand the result, we still need to do something with
                    // the pending tasks
                    foreach (var op in wrapped)
                    {
                        op.Wrapped.Fail(ConnectionFailureType.ProtocolFailure, null);
                        bridge.CompleteSyncOrAsync(op.Wrapped);
                    }
                }
                return(false);
            }
Exemplo n.º 20
0
            public IEnumerable <Message> GetMessages(PhysicalConnection connection)
            {
                ResultBox lastBox = null;

                try
                {
                    // Important: if the server supports EXECABORT, then we can check the pre-conditions (pause there),
                    // which will usually be pretty small and cheap to do - if that passes, we can just isue all the commands
                    // and rely on EXECABORT to kick us if we are being idiotic inside the MULTI. However, if the server does
                    // *not* support EXECABORT, then we need to explicitly check for QUEUED anyway; we might as well defer
                    // checking the preconditions to the same time to avoid having to pause twice. This will mean that on
                    // up-version servers, pre-condition failures exit with UNWATCH; and on down-version servers pre-condition
                    // failures exit with DISCARD - but that's ok : both work fine

                    bool explicitCheckForQueued = !connection.Bridge.ServerEndPoint.GetFeatures().ExecAbort;
                    var  multiplexer            = connection.Multiplexer;

                    // PART 1: issue the pre-conditions
                    if (!IsAborted && conditions.Length != 0)
                    {
                        for (int i = 0; i < conditions.Length; i++)
                        {
                            // need to have locked them before sending them
                            // to guarantee that we see the pulse
                            ResultBox latestBox = conditions[i].GetBox();
                            Monitor.Enter(latestBox);
                            if (lastBox != null)
                            {
                                Monitor.Exit(lastBox);
                            }
                            lastBox = latestBox;
                            foreach (var msg in conditions[i].CreateMessages(Db))
                            {
                                msg.SetNoRedirect(); // need to keep them in the current context only
                                yield return(msg);
                            }
                        }

                        if (!explicitCheckForQueued && lastBox != null)
                        {
                            // need to get those sent ASAP; if they are stuck in the buffers, we die
                            multiplexer.Trace("Flushing and waiting for precondition responses");
                            connection.Flush();
                            if (Monitor.Wait(lastBox, multiplexer.TimeoutMilliseconds))
                            {
                                if (!AreAllConditionsSatisfied(multiplexer))
                                {
                                    command = RedisCommand.UNWATCH; // somebody isn't happy
                                }
                            }
                            else
                            { // timeout running pre-conditions
                                multiplexer.Trace("Timeout checking preconditions");
                                command = RedisCommand.UNWATCH;
                            }
                            Monitor.Exit(lastBox);
                            lastBox = null;
                        }
                    }

                    // PART 2: begin the transaction
                    if (!IsAborted)
                    {
                        multiplexer.Trace("Begining transaction");
                        yield return(Message.Create(-1, CommandFlags.None, RedisCommand.MULTI));
                    }

                    // PART 3: issue the commands
                    if (!IsAborted && operations.Length != 0)
                    {
                        multiplexer.Trace("Issuing transaction operations");

                        foreach (var op in operations)
                        {
                            if (explicitCheckForQueued)
                            {   // need to have locked them before sending them
                                // to guarantee that we see the pulse
                                ResultBox thisBox = op.ResultBox;
                                if (thisBox != null)
                                {
                                    Monitor.Enter(thisBox);
                                    if (lastBox != null)
                                    {
                                        Monitor.Exit(lastBox);
                                    }
                                    lastBox = thisBox;
                                }
                            }
                            yield return(op);
                        }

                        if (explicitCheckForQueued && lastBox != null)
                        {
                            multiplexer.Trace("Flushing and waiting for precondition+queued responses");
                            connection.Flush(); // make sure they get sent, so we can check for QUEUED (and the pre-conditions if necessary)
                            if (Monitor.Wait(lastBox, multiplexer.TimeoutMilliseconds))
                            {
                                if (!AreAllConditionsSatisfied(multiplexer))
                                {
                                    command = RedisCommand.DISCARD;
                                }
                                else
                                {
                                    foreach (var op in operations)
                                    {
                                        if (!op.WasQueued)
                                        {
                                            multiplexer.Trace("Aborting: operation was not queued: " + op.Command);
                                            command = RedisCommand.DISCARD;
                                            break;
                                        }
                                    }
                                }
                                multiplexer.Trace("Confirmed: QUEUED x " + operations.Length);
                            }
                            else
                            {
                                multiplexer.Trace("Aborting: timeout checking queued messages");
                                command = RedisCommand.DISCARD;
                            }
                            Monitor.Exit(lastBox);
                            lastBox = null;
                        }
                    }
                }
                finally
                {
                    if (lastBox != null)
                    {
                        Monitor.Exit(lastBox);
                    }
                }
                if (IsAborted)
                {
                    connection.Multiplexer.Trace("Aborting: canceling wrapped messages");
                    var bridge = connection.Bridge;
                    foreach (var op in operations)
                    {
                        op.Wrapped.Cancel();
                        bridge.CompleteSyncOrAsync(op.Wrapped);
                    }
                }
                connection.Multiplexer.Trace("End ot transaction: " + Command);
                yield return(this); // acts as either an EXEC or an UNWATCH, depending on "aborted"
            }
Exemplo n.º 21
0
 internal override void WriteImpl(PhysicalConnection physical)
 {
     physical.WriteHeader(Command, 1);
     physical.Write(Key);
 }
Exemplo n.º 22
0
        internal WriteResult WriteQueue(int maxWork)
        {
            bool weAreWriter        = false;
            PhysicalConnection conn = null;

            try
            {
                Trace("Writing queue from bridge");

                weAreWriter = Interlocked.CompareExchange(ref activeWriters, 1, 0) == 0;
                if (!weAreWriter)
                {
                    Trace("(aborting: existing writer)");
                    return(WriteResult.CompetingWriter);
                }

                conn = GetConnection(null);
                if (conn == null)
                {
                    AbortUnsent();
                    Trace("Connection not available; exiting");
                    return(WriteResult.NoConnection);
                }

                Message last;
                int     count = 0;
                while (true)
                {
                    var next = queue.Dequeue();
                    if (next == null)
                    {
                        Trace("Nothing to write; exiting");
                        if (count == 0)
                        {
                            conn.Flush(); // only flush on an empty run
                            return(WriteResult.NothingToDo);
                        }
                        return(WriteResult.QueueEmptyAfterWrite);
                    }
                    last = next;

                    Trace("Now pending: " + GetPendingCount());
                    if (!WriteMessageDirect(conn, next))
                    {
                        AbortUnsent();
                        Trace("write failed; connection is toast; exiting");
                        return(WriteResult.NoConnection);
                    }
                    count++;
                    if (maxWork > 0 && count >= maxWork)
                    {
                        Trace("Work limit; exiting");
                        Trace(last != null, "Flushed up to: " + last);
                        conn.Flush();
                        break;
                    }
                }
            }
            catch (IOException ex)
            {
                if (conn != null)
                {
                    conn.RecordConnectionFailed(ConnectionFailureType.SocketFailure, ex);
                    conn = null;
                }
                AbortUnsent();
            }
            catch (Exception ex)
            {
                AbortUnsent();
                OnInternalError(ex);
            }
            finally
            {
                if (weAreWriter)
                {
                    Interlocked.Exchange(ref activeWriters, 0);
                    Trace("Exiting writer");
                }
            }
            return(queue.Any() ? WriteResult.MoreWork : WriteResult.QueueEmptyAfterWrite);
        }
Exemplo n.º 23
0
        private bool WriteMessageToServer(PhysicalConnection connection, Message message)
        {
            if (message == null)
            {
                return(true);
            }

            try
            {
                var  cmd          = message.Command;
                bool isMasterOnly = message.IsMasterOnly();
                if (isMasterOnly && ServerEndPoint.IsSlave && (ServerEndPoint.SlaveReadOnly || !ServerEndPoint.AllowSlaveWrites))
                {
                    throw ExceptionFactory.MasterOnly(Multiplexer.IncludeDetailInExceptions, message.Command, message, ServerEndPoint);
                }

                SelectDatabase(connection, message);

                if (!connection.TransactionActive)
                {
                    var readmode = connection.GetReadModeCommand(isMasterOnly);
                    if (readmode != null)
                    {
                        connection.Enqueue(readmode);
                        readmode.WriteTo(connection);
                        readmode.SetRequestSent();
                        IncrementOpCount();
                    }

                    if (message.IsAsking)
                    {
                        var asking = ReusableAskingCommand;
                        connection.Enqueue(asking);
                        asking.WriteImpl(connection);
                        asking.SetRequestSent();
                        IncrementOpCount();
                    }
                }
                switch (cmd)
                {
                case RedisCommand.WATCH:
                case RedisCommand.MULTI:
                    connection.TransactionActive = true;
                    break;

                case RedisCommand.UNWATCH:
                case RedisCommand.EXEC:
                case RedisCommand.DISCARD:
                    connection.TransactionActive = false;
                    break;
                }

                connection.Enqueue(message);
                message.WriteImpl(connection);
                message.SetRequestSent();
                IncrementOpCount();

                // some commands smash our ability to trust the database; some commands
                // demand an immediate flush
                switch (cmd)
                {
                case RedisCommand.EVAL:
                case RedisCommand.EVALSHA:
                    if (!ServerEndPoint.GetFeatures().ScriptingDatabaseSafe)
                    {
                        connection.SetUnknownDatabase();
                    }
                    break;

                case RedisCommand.DISCARD:
                case RedisCommand.EXEC:
                    connection.SetUnknownDatabase();
                    break;
                }
                return(true);
            }
            catch (RedisCommandException ex)
            {
                Trace("Write failed: " + ex.Message);
                message.Fail(ConnectionFailureType.ProtocolFailure, ex);
                CompleteSyncOrAsync(message);
                // this failed without actually writing; we're OK with that... unless there's a transaction

                if (connection != null && connection.TransactionActive)
                {
                    // we left it in a broken state; need to kill the connection
                    connection.RecordConnectionFailed(ConnectionFailureType.ProtocolFailure, ex);
                    return(false);
                }
                return(true);
            }
            catch (Exception ex)
            {
                Trace("Write failed: " + ex.Message);
                message.Fail(ConnectionFailureType.InternalFailure, ex);
                CompleteSyncOrAsync(message);

                // we're not sure *what* happened here; probably an IOException; kill the connection
                connection?.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
                return(false);
            }
        }