Example #1
0
        internal Message GetSelectDatabaseCommand(int targetDatabase, Message message)
        {
            if (targetDatabase < 0)
            {
                return(null);
            }
            if (targetDatabase != currentDatabase)
            {
                var serverEndpoint = Bridge.ServerEndPoint;
                int available      = serverEndpoint.Databases;

                if (!serverEndpoint.HasDatabases) // only db0 is available on cluster/twemproxy
                {
                    if (targetDatabase != 0)
                    { // should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
                        throw new RedisCommandException("Multiple databases are not supported on this server; cannot switch to database: " + targetDatabase);
                    }
                    return(null);
                }

                if (message.Command == RedisCommand.SELECT)
                {
                    // this could come from an EVAL/EVALSHA inside a transaction, for example; we'll accept it
                    Bridge.Trace("Switching database: " + targetDatabase);
                    currentDatabase = targetDatabase;
                    return(null);
                }

                if (TransactionActive)
                {// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
                    throw new RedisCommandException("Multiple databases inside a transaction are not currently supported: " + targetDatabase);
                }

                if (available != 0 && targetDatabase >= available) // we positively know it is out of range
                {
                    throw ExceptionFactory.DatabaseOutfRange(Multiplexer.IncludeDetailInExceptions, targetDatabase, message, serverEndpoint);
                }
                Bridge.Trace("Switching database: " + targetDatabase);
                currentDatabase = targetDatabase;
                return(GetSelectDatabaseCommand(targetDatabase));
            }
            return(null);
        }
        /// <summary>
        /// Begins profiling for the given context.
        ///
        /// If the same context object is returned by the registered IProfiler, the IProfiledCommands
        /// will be associated with each other.
        ///
        /// Call FinishProfiling with the same context to get the assocated commands.
        ///
        /// Note that forContext cannot be a WeakReference or a WeakReference&lt;T&gt;
        /// </summary>
        public void BeginProfiling(object forContext)
        {
            if (profiler == null)
            {
                throw new InvalidOperationException("Cannot begin profiling if no IProfiler has been registered with RegisterProfiler");
            }
            if (forContext == null)
            {
                throw new ArgumentNullException(nameof(forContext));
            }
            if (forContext is WeakReference)
            {
                throw new ArgumentException("Context object cannot be a WeakReference", nameof(forContext));
            }

            if (!profiledCommands.TryCreate(forContext))
            {
                throw ExceptionFactory.BeganProfilingWithDuplicateContext(forContext);
            }
        }
Example #3
0
        internal const int REDIS_MAX_ARGS = 1024 * 1024; // there is a <= 1024*1024 max constraint inside redis itself: https://github.com/antirez/redis/blob/6c60526db91e23fb2d666fc52facc9a11780a2a3/src/networking.c#L1024

        internal void WriteHeader(string command, int arguments)
        {
            if (arguments >= REDIS_MAX_ARGS) // using >= here because we will be adding 1 for the command itself (which is an arg for the purposes of the multi-bulk protocol)
            {
                throw ExceptionFactory.TooManyArgs(Multiplexer.IncludeDetailInExceptions, command, null, Bridge.ServerEndPoint, arguments + 1);
            }
            var commandBytes = Multiplexer.CommandMap.GetBytes(command);

            if (commandBytes == null)
            {
                throw ExceptionFactory.CommandDisabled(Multiplexer.IncludeDetailInExceptions, command, null, Bridge.ServerEndPoint);
            }
            outStream.WriteByte((byte)'*');

            // remember the time of the first write that still not followed by read
            Interlocked.CompareExchange(ref firstUnansweredWriteTickCount, Environment.TickCount, 0);

            WriteRaw(outStream, arguments + 1);
            WriteUnified(outStream, commandBytes);
        }
        protected Message(int db, CommandFlags flags, RedisCommand command)
        {
            bool dbNeeded = Message.RequiresDatabase(command);

            if (db < 0)
            {
                if (dbNeeded)
                {
                    throw ExceptionFactory.DatabaseRequired(false, command);
                }
            }
            else
            {
                if (!dbNeeded)
                {
                    throw ExceptionFactory.DatabaseNotRequired(false, command);
                }
            }

            if (IsMasterOnly(command))
            {
                switch (GetMasterSlaveFlags(flags))
                {
                case CommandFlags.DemandSlave:
                    throw ExceptionFactory.MasterOnly(false, command, null, null);

                case CommandFlags.DemandMaster:
                    // already fine as-is
                    break;

                case CommandFlags.PreferMaster:
                case CommandFlags.PreferSlave:
                default:     // we will run this on the master, then
                    flags = SetMasterSlaveFlags(flags, CommandFlags.DemandMaster);
                    break;
                }
            }
            this.Db      = db;
            this.command = command;
            this.flags   = flags & UserSelectableFlags;
        }
        public ServerEndPoint Select(Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            int slot = NoSlot;

            switch (serverType)
            {
            case ServerType.Cluster:
            case ServerType.Twemproxy:     // strictly speaking twemproxy uses a different hashing algo, but the hash-tag behavior is
                                           // the same, so this does a pretty good job of spotting illegal commands before sending them

                slot = message.GetHashSlot(this);
                if (slot == MultipleSlots)
                {
                    throw ExceptionFactory.MultiSlot(multiplexer.IncludeDetailInExceptions, message);
                }
                break;
            }
            return(Select(slot, message.Command, message.Flags));
        }
 internal override Task <T> ExecuteAsync <T>(Message message, ResultProcessor <T> processor, ServerEndPoint server = null)
 {   // inject our expected server automatically
     if (server == null)
     {
         server = this.server;
     }
     FixFlags(message, server);
     if (!server.IsConnected)
     {
         if (message == null)
         {
             return(CompletedTask <T> .Default(asyncState));
         }
         if (message.IsFireAndForget)
         {
             return(CompletedTask <T> .Default(null));                       // F+F explicitly does not get async-state
         }
         // no need to deny exec-sync here; will be complete before they see if
         var tcs = TaskSource.Create <T>(asyncState);
         ConnectionMultiplexer.ThrowFailed(tcs, ExceptionFactory.NoConnectionAvailable(multiplexer.IncludeDetailInExceptions, message.Command, message, server));
         return(tcs.Task);
     }
     return(base.ExecuteAsync <T>(message, processor, server));
 }
Example #7
0
        private RawResult ReadArray(byte[] buffer, ref int offset, ref int count)
        {
            var itemCount = ReadLineTerminatedString(ResultType.Integer, buffer, ref offset, ref count);

            if (itemCount.HasValue)
            {
                long i64;
                if (!itemCount.TryGetInt64(out i64))
                {
                    throw ExceptionFactory.ConnectionFailure(Multiplexer.IncludeDetailInExceptions, ConnectionFailureType.ProtocolFailure, "Invalid array length", Bridge.ServerEndPoint);
                }
                int itemCountActual = checked ((int)i64);

                if (itemCountActual < 0)
                {
                    //for null response by command like EXEC, RESP array: *-1\r\n
                    return(new RawResult(ResultType.SimpleString, null, 0, 0));
                }
                else if (itemCountActual == 0)
                {
                    //for zero array response by command like SCAN, Resp array: *0\r\n
                    return(RawResult.EmptyArray);
                }

                var arr = new RawResult[itemCountActual];
                for (int i = 0; i < itemCountActual; i++)
                {
                    if (!(arr[i] = TryParseResult(buffer, ref offset, ref count)).HasValue)
                    {
                        return(RawResult.Nil);
                    }
                }
                return(new RawResult(arr));
            }
            return(RawResult.Nil);
        }
        /// <summary>
        /// Stops profiling for the given context, returns all IProfiledCommands associated.
        ///
        /// By default this may do a sweep for dead profiling contexts, you can disable this by passing "allowCleanupSweep: false".
        /// </summary>
        /// <param name="forContext">The context to begin profiling.</param>
        /// <param name="allowCleanupSweep">Whether to allow cleanup of old profiling sessions.</param>
        public ProfiledCommandEnumerable FinishProfiling(object forContext, bool allowCleanupSweep = true)
        {
            if (profiler == null)
            {
                throw new InvalidOperationException("Cannot begin profiling if no IProfiler has been registered with RegisterProfiler");
            }
            if (forContext == null)
            {
                throw new ArgumentNullException(nameof(forContext));
            }

            if (!profiledCommands.TryRemove(forContext, out ProfiledCommandEnumerable ret))
            {
                throw ExceptionFactory.FinishedProfilingWithInvalidContext(forContext);
            }

            // conditional, because it could hurt and that may sometimes be unacceptable
            if (allowCleanupSweep)
            {
                profiledCommands.TryCleanup();
            }

            return(ret);
        }
Example #9
0
        internal void OnHeartbeat(bool ifConnectedOnly)
        {
            bool runThisTime = false;

            try
            {
                runThisTime = !isDisposed && Interlocked.CompareExchange(ref beating, 1, 0) == 0;
                if (!runThisTime)
                {
                    return;
                }

                uint index          = (uint)Interlocked.Increment(ref profileLogIndex);
                long newSampleCount = Interlocked.Read(ref operationCount);
                Interlocked.Exchange(ref profileLog[index % ProfileLogSamples], newSampleCount);
                Interlocked.Exchange(ref profileLastLog, newSampleCount);
                Trace("OnHeartbeat: " + (State)state);
                switch (state)
                {
                case (int)State.Connecting:
                    int  connectTimeMilliseconds = unchecked (Environment.TickCount - Thread.VolatileRead(ref connectStartTicks));
                    bool shouldRetry             = Multiplexer.RawConfig.ReconnectRetryPolicy.ShouldRetry(Interlocked.Read(ref connectTimeoutRetryCount), connectTimeMilliseconds);
                    if (shouldRetry)
                    {
                        Interlocked.Increment(ref connectTimeoutRetryCount);
                        LastException = ExceptionFactory.UnableToConnect(Multiplexer, "ConnectTimeout");
                        Trace("Aborting connect");
                        // abort and reconnect
                        var snapshot = physical;
                        OnDisconnected(ConnectionFailureType.UnableToConnect, snapshot, out bool isCurrent, out State oldState);
                        using (snapshot) { }     // dispose etc
                        TryConnect(null);
                    }
                    break;

                case (int)State.ConnectedEstablishing:
                case (int)State.ConnectedEstablished:
                    var tmp = physical;
                    if (tmp != null)
                    {
                        if (state == (int)State.ConnectedEstablished)
                        {
                            Interlocked.Exchange(ref connectTimeoutRetryCount, 0);
                            tmp.BridgeCouldBeNull?.ServerEndPoint?.ClearUnselectable(UnselectableFlags.DidNotRespond);
                        }
                        tmp.OnBridgeHeartbeat();
                        int writeEverySeconds  = ServerEndPoint.WriteEverySeconds,
                            checkConfigSeconds = Multiplexer.RawConfig.ConfigCheckSeconds;

                        if (state == (int)State.ConnectedEstablished && ConnectionType == ConnectionType.Interactive &&
                            checkConfigSeconds > 0 && ServerEndPoint.LastInfoReplicationCheckSecondsAgo >= checkConfigSeconds &&
                            ServerEndPoint.CheckInfoReplication())
                        {
                            // that serves as a keep-alive, if it is accepted
                        }
                        else if (writeEverySeconds > 0 && tmp.LastWriteSecondsAgo >= writeEverySeconds)
                        {
                            Trace("OnHeartbeat - overdue");
                            if (state == (int)State.ConnectedEstablished)
                            {
                                KeepAlive();
                            }
                            else
                            {
                                OnDisconnected(ConnectionFailureType.SocketFailure, tmp, out bool ignore, out State oldState);
                            }
                        }
                        else if (writeEverySeconds <= 0 && tmp.IsIdle() &&
                                 tmp.LastWriteSecondsAgo > 2 &&
                                 tmp.GetSentAwaitingResponseCount() != 0)
                        {
                            // there's a chance this is a dead socket; sending data will shake that
                            // up a bit, so if we have an empty unsent queue and a non-empty sent
                            // queue, test the socket
                            KeepAlive();
                        }
                    }
                    break;

                case (int)State.Disconnected:
                    Interlocked.Exchange(ref connectTimeoutRetryCount, 0);
                    if (!ifConnectedOnly)
                    {
                        Multiplexer.Trace("Resurrecting " + ToString());
                        Multiplexer.OnResurrecting(ServerEndPoint?.EndPoint, ConnectionType);
                        GetConnection(null);
                    }
                    break;

                default:
                    Interlocked.Exchange(ref connectTimeoutRetryCount, 0);
                    break;
                }
            }
            catch (Exception ex)
            {
                OnInternalError(ex);
                Trace("OnHeartbeat error: " + ex.Message);
            }
            finally
            {
                if (runThisTime)
                {
                    Interlocked.Exchange(ref beating, 0);
                }
            }
        }
Example #10
0
        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);
                this.CompleteSyncOrAsync(message);
                // 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);
                this.CompleteSyncOrAsync(message);

                // we're not sure *what* happened here; probably an IOException; kill the connection
                connection?.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
                return(WriteResult.WriteFailure);
            }
        }
Example #11
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
                if (connection != null)
                {
                    connection.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
                }
                return(false);
            }
        }