private WriteResult FailDueToNoConnection(Message message)
 {
     message.Cancel();
     Multiplexer?.OnMessageFaulted(message, null);
     message.Complete();
     return(WriteResult.NoConnectionAvailable);
 }
Ejemplo n.º 2
0
 private WriteResult TimedOutBeforeWrite(Message message)
 {
     message.Cancel();
     Multiplexer?.OnMessageFaulted(message, null);
     message.Complete();
     return(WriteResult.TimeoutBeforeWrite);
 }
        internal WriteResult WriteMessageTakingWriteLockSync(PhysicalConnection physical, Message message)
        {
            Trace("Writing: " + message);
            message.SetEnqueued(physical); // this also records the read/write stats at this point

            LockToken token = default;

            try
            {
                token = _singleWriterMutex.TryWait(WaitOptions.NoDelay);
                if (!token.Success)
                {
                    // we can't get it *instantaneously*; is there
                    // perhaps a backlog and active backlog processor?
                    bool haveBacklog;
                    lock (_backlog)
                    {
                        haveBacklog = _backlog.Count != 0;
                    }
                    if (haveBacklog)
                    {
                        PushToBacklog(message);
                        return(WriteResult.Success); // queued counts as success
                    }

                    // no backlog... try to wait with the timeout;
                    // if we *still* can't get it: that counts as
                    // an actual timeout
                    token = _singleWriterMutex.TryWait();
                    if (!token.Success)
                    {
                        message.Cancel();
                        Multiplexer?.OnMessageFaulted(message, null);
                        message.Complete();
                        return(WriteResult.TimeoutBeforeWrite);
                    }
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
#pragma warning disable CS0618
                    result = physical.FlushSync(false, TimeoutMilliseconds);
#pragma warning restore CS0618
                }

                UnmarkActiveMessage(message);
                physical.SetIdle();
                return(result);
            }
            catch (Exception ex) { return(HandleWriteException(message, ex)); }
            finally { token.Dispose(); }
        }
        private WriteResult WriteMessageInsideLock(PhysicalConnection physical, Message message)
        {
            WriteResult result;
            var         existingMessage = Interlocked.CompareExchange(ref _activeMesssage, message, null);

            if (existingMessage != null)
            {
                Multiplexer?.OnInfoMessage($"reentrant call to WriteMessageTakingWriteLock for {message.CommandAndKey}, {existingMessage.CommandAndKey} is still active");
                return(WriteResult.NoConnectionAvailable);
            }
            physical.SetWriting();
            var messageIsSent = false;

            if (message is IMultiMessage)
            {
                SelectDatabaseInsideWriteLock(physical, message); // need to switch database *before* the transaction
                foreach (var subCommand in ((IMultiMessage)message).GetMessages(physical))
                {
                    result = WriteMessageToServerInsideWriteLock(physical, subCommand);
                    if (result != WriteResult.Success)
                    {
                        // we screwed up; abort; note that WriteMessageToServer already
                        // killed the underlying connection
                        Trace("Unable to write to server");
                        message.Fail(ConnectionFailureType.ProtocolFailure, null, "failure before write: " + result.ToString());
                        message.Complete();
                        return(result);
                    }
                    //The parent message (next) may be returned from GetMessages
                    //and should not be marked as sent again below
                    messageIsSent = messageIsSent || subCommand == message;
                }
                if (!messageIsSent)
                {
                    message.SetRequestSent(); // well, it was attempted, at least...
                }

                return(WriteResult.Success);
            }
            else
            {
                return(WriteMessageToServerInsideWriteLock(physical, message));
            }
        }
 private WriteResult QueueOrFailMessage(Message message)
 {
     if (message.IsInternalCall && message.Command != RedisCommand.QUIT)
     {
         // you can go in the queue, but we won't be starting
         // a worker, because the handshake has not completed
         lock (_backlog)
         {
             _backlog.Enqueue(message);
         }
         message.SetEnqueued(null);
         return(WriteResult.Success); // we'll take it...
     }
     else
     {
         // sorry, we're just not ready for you yet;
         message.Cancel();
         Multiplexer?.OnMessageFaulted(message, null);
         message.Complete();
         return(WriteResult.NoConnectionAvailable);
     }
 }
        private WriteResult WriteMessageToServerInsideWriteLock(PhysicalConnection connection, Message message)
        {
            if (message == null)
            {
                return(WriteResult.Success);                 // for some definition of success
            }
            bool isQueued = false;

            try
            {
                var cmd = message.Command;
                LastCommand = cmd;
                bool isMasterOnly = message.IsMasterOnly();

                if (isMasterOnly && ServerEndPoint.IsSlave && (ServerEndPoint.SlaveReadOnly || !ServerEndPoint.AllowSlaveWrites))
                {
                    throw ExceptionFactory.MasterOnly(Multiplexer.IncludeDetailInExceptions, message.Command, message, ServerEndPoint);
                }
                switch (cmd)
                {
                case RedisCommand.QUIT:
                    connection.RecordQuit();
                    break;

                case RedisCommand.EXEC:
                    Multiplexer.OnPreTransactionExec(message);     // testing purposes, to force certain errors
                    break;
                }

                SelectDatabaseInsideWriteLock(connection, message);

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

                    if (message.IsAsking)
                    {
                        var asking = ReusableAskingCommand;
                        connection.EnqueueInsideWriteLock(asking);
                        asking.WriteTo(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.EnqueueInsideWriteLock(message);
                isQueued = true;
                message.WriteTo(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.UNKNOWN:
                case RedisCommand.DISCARD:
                case RedisCommand.EXEC:
                    connection.SetUnknownDatabase();
                    break;
                }
                return(WriteResult.Success);
            }
            catch (RedisCommandException ex) when(!isQueued)
            {
                Trace("Write failed: " + ex.Message);
                message.Fail(ConnectionFailureType.InternalFailure, ex, null);
                message.Complete();
                // this failed without actually writing; we're OK with that... unless there's a transaction

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

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