Пример #1
0
        void Handshake(PhysicalConnection connection)
        {
            multiplexer.Trace("Server handshake");
            if (connection == null)
            {
                multiplexer.Trace("No connection!?");
                return;
            }
            Message msg;
            string  password = multiplexer.RawConfig.Password;

            if (!string.IsNullOrWhiteSpace(password))
            {
                multiplexer.Trace("Sending 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.Trace("Setting client name: " + 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.Trace("Auto-configure...");
                AutoConfigure(connection);
            }
            multiplexer.Trace("Sending critical tracer");
            WriteDirectOrQueueFireAndForget(connection, GetTracerMessage(true), 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);
                }
            }

            connection.Flush();
        }
            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(SharedMulti);
                    }

                    // 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"
            }
Пример #3
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);
        }