// 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 } }
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; } }
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(); } } }
private PhysicalConnection GetConnection(TextWriter log) { if (state == (int)State.Disconnected) { try { if (!multiplexer.IsDisposed) { Multiplexer.LogLocked(log, "Connecting {0}...", Name); Multiplexer.Trace("Connecting...", Name); if (ChangeState(State.Disconnected, State.Connecting)) { Interlocked.Increment(ref socketCount); Interlocked.Exchange(ref connectStartTicks, Environment.TickCount); // separate creation and connection for case when connection completes synchronously // in that case PhysicalConnection will call back to PhysicalBridge, and most of PhysicalBridge methods assumes that physical is not null; physical = new PhysicalConnection(this); physical.BeginConnect(log); } } return null; } catch (Exception ex) { Multiplexer.LogLocked(log, "Connect {0} failed: {1}", Name, ex.Message); Multiplexer.Trace("Connect failed: " + ex.Message, Name); ChangeState(State.Disconnected); OnInternalError(ex); throw; } } return physical; }
/// <summary> /// This writes a message **directly** to the output stream; note /// that this ignores the queue, so should only be used *either* /// from the regular dequeue loop, *or* from the "I've just /// connected" handshake (when there is no dequeue loop) - otherwise, /// you can pretty much assume you're going to destroy the stream /// </summary> internal bool WriteMessageDirect(PhysicalConnection tmp, Message next) { Trace("Writing: " + next); if (next is IMultiMessage) { SelectDatabase(tmp, next); // need to switch database *before* the transaction foreach (var subCommand in ((IMultiMessage)next).GetMessages(tmp)) { if (!WriteMessageToServer(tmp, subCommand)) { // we screwed up; abort; note that WriteMessageToServer already // killed the underlying connection Trace("Unable to write to server"); next.Fail(ConnectionFailureType.ProtocolFailure, null); CompleteSyncOrAsync(next); return false; } } next.SetRequestSent(); return true; } else { return WriteMessageToServer(tmp, next); } }
internal void RemovePhysical(PhysicalConnection connection) { #pragma warning disable 0420 Interlocked.CompareExchange(ref physical, null, connection); #pragma warning restore 0420 }
internal void OnFullyEstablished(PhysicalConnection connection) { Trace("OnFullyEstablished"); if (physical == connection && !isDisposed && ChangeState(State.ConnectedEstablishing, State.ConnectedEstablished)) { reportNextFailure = reconfigureNextFailure = true; Interlocked.Exchange(ref failConnectCount, 0); serverEndPoint.OnFullyEstablished(connection); multiplexer.RequestWrite(this, true); if(connectionType == ConnectionType.Interactive) serverEndPoint.CheckInfoReplication(); } else { try { connection.Dispose(); } catch { } } }
internal void OnDisconnected(ConnectionFailureType failureType, PhysicalConnection connection, out bool isCurrent, out State oldState) { Trace("OnDisconnected"); // if the next thing in the pipe is a PING, we can tell it that we failed (this really helps spot doomed connects) // note that for simplicity we haven't removed it from the queue; that's OK int count; var ping = queue.PeekPing(out count); if (ping != null) { Trace("Marking PING as failed (queue length: " + count + ")"); ping.Fail(failureType, null); CompleteSyncOrAsync(ping); } oldState = default(State); // only defined when isCurrent = true if (isCurrent = (physical == connection)) { Trace("Bridge noting disconnect from active connection" + (isDisposed ? " (disposed)" : "")); oldState = ChangeState(State.Disconnected); physical = null; if (!isDisposed && Interlocked.Increment(ref failConnectCount) == 1) { GetConnection(null); // try to connect immediately } } else if (physical == null) { Trace("Bridge noting disconnect (already terminated)"); } else { Trace("Bridge noting disconnect, but from different connection"); } }
internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailureType failureType, Exception innerException) { if (reportNextFailure) { reportNextFailure = false; // until it is restored var endpoint = serverEndPoint.EndPoint; multiplexer.OnConnectionFailed(endpoint, connectionType, failureType, innerException, reconfigureNextFailure); } }
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 { } } }
public void Dispose() { isDisposed = true; using (var tmp = physical) { physical = null; } }
private PhysicalConnection GetConnection() { if (state == (int)State.Disconnected) { try { if (!multiplexer.IsDisposed) { Multiplexer.Trace("Connecting...", Name); if (ChangeState(State.Disconnected, State.Connecting)) { Interlocked.Increment(ref socketCount); physical = new PhysicalConnection(this); } } return null; } catch (Exception ex) { Multiplexer.Trace("Connect failed: " + ex.Message, Name); ChangeState(State.Disconnected); OnInternalError(ex); throw; } } return physical; }