public void NodeDisposeTest() { // Memcache client config var config = new MemcacheClientConfiguration { TransportFactory = (_, __, r, s, ___, ____) => { var transport = new TransportMock(r) { IsAlive = true, Setup = s }; if (s != null) { s(transport); } return(transport); }, PoolSize = 1, }; var node = new MemcacheNode(null, config); // TransportMock does not put back the transport in the pool after the TrySend Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request through the node"); // Dispose the node after a certain delay ThreadPool.QueueUserWorkItem((o) => { Thread.Sleep(500); node.Dispose(); }); // The following TrySend will block because the transport pool is empty Assert.DoesNotThrow(() => node.TrySend(new NoOpRequest(), 1000), "The TrySend should not throw an exception"); }
public CouchbaseCluster(MemcacheClientConfiguration configuration, string bucket, IPEndPoint[] configurationHosts) { if (configurationHosts.Length == 0) throw new ArgumentException("There should be at least one value in the list", "configurationHosts"); _isInitialized = false; _linesStreamReader = null; _webResponse = null; _configuration = configuration; if (_configuration.Authenticator == null) _configuration.Authenticator = MemcacheClientConfiguration.SaslPlainAuthenticatorFactory(string.Empty, bucket, string.Empty); _bucket = bucket; _currentConfigurationHost = 0; _configurationHosts = configurationHosts; _memcacheNodes = new Dictionary<string, IMemcacheNode>(); _locator = null; _connectionTimer = new Timer(_ => ConnectToConfigurationStream(), null, Timeout.Infinite, Timeout.Infinite); _receivedInitialConfigurationBarrier = new ManualResetEventSlim(); }
public void TransportDeadThenAliveTest() { IMemcacheNode theNode = null; FakeTransport transport = new FakeTransport(); transport.Kill(); // Memcache client config var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportFactory = transport.Factory, NodeFactory = (e, c) => MemcacheClientConfiguration.DefaultNodeFactory(e, c), PoolSize = 1, }; theNode = config.NodeFactory(null, config); Assert.IsFalse(theNode.IsDead, "The node should be alive before any failure"); Assert.IsFalse(theNode.TrySend(null, 1), "The TrySend should fail with a broken transport"); Assert.IsTrue(theNode.IsDead, "The node should be dead after the first send failed"); transport.Resurect(); Assert.IsFalse(theNode.IsDead, "The node should be alive after the transport resurected"); Assert.IsTrue(theNode.TrySend(null, 1), "The TrySend should be able to send a request after the transport is up again"); transport.Kill(); Assert.IsFalse(theNode.TrySend(null, 1), "The TrySend should fail with a killed transport"); Assert.IsTrue(theNode.IsDead, "The node should be dead after the a send failed"); }
public void SerializationThrows() { var serializerMoq = new Mock<ISerializer<TestType>>(); serializerMoq.Setup(s => s.FromBytes(It.IsAny<byte[]>())).Throws(new SerializationException()); serializerMoq.Setup(s => s.ToBytes(It.IsAny<TestType>())).Throws(new SerializationException()); serializerMoq.Setup(s => s.TypeFlag).Returns(314); Exception raised = null; var config = new MemcacheClientConfiguration { ClusterFactory = c => _clusterMock, }; var serialiserMoqObj = serializerMoq.Object; config.SetSerializer(serialiserMoqObj); var client = new MemcacheClient(config); client.CallbackError += e => raised = e; // test that the throws of the serializer is synchronously propagated Assert.Throws(typeof(SerializationException), () => client.Set("Hello", new TestType(), TimeSpan.Zero)); // test that the failing serializer does not synchronously throws but sends a CallbackError event _responseHeader = new MemcacheResponseHeader { ExtraLength = 4, TotalBodyLength = 4, Opcode = Opcode.Get, }; _extra = new byte[] { 0, 0, 0, 0}; Assert.True(client.Get("Hello", (Status s, TestType v) => { })); Assert.IsNotNull(raised); Assert.IsInstanceOf<SerializationException>(raised); }
public MemcacheNode(EndPoint endPoint, MemcacheClientConfiguration configuration, IOngoingDispose clientDispose) { if (configuration == null) { throw new ArgumentException("Client config should not be null"); } _configuration = configuration; _endPoint = endPoint; _clientDispose = clientDispose; _tokenSource = new CancellationTokenSource(); _transportPool = new BlockingCollection <IMemcacheTransport>(new ConcurrentStack <IMemcacheTransport>()); for (int i = 0; i < configuration.PoolSize; ++i) { var transport = (configuration.TransportFactory ?? MemcacheTransport.DefaultAllocator) (endPoint, configuration, RegisterEvents, TransportAvailable, false, clientDispose); TransportAvailable(transport); } }
public CouchbaseCluster(MemcacheClientConfiguration configuration, string bucket, IPEndPoint[] configurationHosts) { if (configurationHosts.Length == 0) { throw new ArgumentException("There should be at least one value in the list", "configurationHosts"); } _isInitialized = false; _linesStreamReader = null; _webResponse = null; _configuration = configuration; if (_configuration.Authenticator == null) { _configuration.Authenticator = MemcacheClientConfiguration.SaslPlainAuthenticatorFactory(string.Empty, bucket, string.Empty); } _bucket = bucket; _currentConfigurationHost = 0; _configurationHosts = configurationHosts; _memcacheNodes = new Dictionary <string, IMemcacheNode>(); _locator = null; _connectionTimer = new Timer(_ => ConnectToConfigurationStream(), null, Timeout.Infinite, Timeout.Infinite); _receivedInitialConfigurationBarrier = new ManualResetEventSlim(); }
public void NodeDisposeTest() { // Memcache client config var config = new MemcacheClientConfiguration { TransportFactory = (_, __, r, s, ___, ____) => { var transport = new TransportMock(r) { IsAlive = true, Setup = s }; if (s != null) s(transport); return transport; }, PoolSize = 1, }; var node = new MemcacheNode(null, config); // TransportMock does not put back the transport in the pool after the TrySend Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request through the node"); // Dispose the node after a certain delay ThreadPool.QueueUserWorkItem((o) => { Thread.Sleep(500); node.Dispose(); }); // The following TrySend will block because the transport pool is empty Assert.DoesNotThrow(() => node.TrySend(new NoOpRequest(), 1000), "The TrySend should not throw an exception"); }
/// <summary> /// Ctor, intialize things ... /// </summary> /// <param name="endpoint" /> /// <param name="clientConfig">The client configuration</param> /// <param name="registerEvents">Delegate to call to register the transport</param> /// <param name="transportAvailable">Delegate to call when the transport is alive</param> /// <param name="planToConnect">If true, connect in a timer handler started immediately and call transportAvailable. /// Otherwise, the transport will connect synchronously at the first request</param> /// <param name="nodeClosing">Interface to check if the node is being disposed of</param> public MemcacheTransport(EndPoint endpoint, MemcacheClientConfiguration clientConfig, Action<IMemcacheTransport> registerEvents, Action<IMemcacheTransport> transportAvailable, bool planToConnect, Func<bool> nodeClosing) { if (clientConfig == null) throw new ArgumentException("Client config should not be null"); _endPoint = endpoint; _clientConfig = clientConfig; _registerEvents = registerEvents; _transportAvailable = transportAvailable; _nodeClosing = nodeClosing; _connectTimer = new Timer(TryConnect); _initialized = false; _registerEvents(this); _pendingRequests = new ConcurrentQueue<IMemcacheRequest>(); _sendAsynchEvtArgs = new SocketAsyncEventArgs(); _sendAsynchEvtArgs.Completed += OnSendRequestComplete; _receiveHeaderAsynchEvtArgs = new SocketAsyncEventArgs(); _receiveHeaderAsynchEvtArgs.SetBuffer(new byte[MemcacheResponseHeader.Size], 0, MemcacheResponseHeader.Size); _receiveHeaderAsynchEvtArgs.Completed += OnReceiveHeaderComplete; _receiveBodyAsynchEvtArgs = new SocketAsyncEventArgs(); _receiveBodyAsynchEvtArgs.Completed += OnReceiveBodyComplete; if (planToConnect) _connectTimer.Change(0, Timeout.Infinite); else if (transportAvailable != null) transportAvailable(this); _alive = !planToConnect; }
/// <summary> /// Ctor, intialize things ... /// </summary> /// <param name="endpoint" /> /// <param name="clientConfig">The client configuration</param> /// <param name="registerEvents">Delegate to call to register the transport</param> /// <param name="transportAvailable">Delegate to call when the transport is alive</param> /// <param name="planToConnect">If true, connect in a timer handler started immediately and call transportAvailable. /// Otherwise, the transport will connect synchronously at the first request</param> /// <param name="nodeClosing">Interface to check if the node is being disposed of</param> public MemcacheTransport(EndPoint endpoint, MemcacheClientConfiguration clientConfig, Action <IMemcacheTransport> registerEvents, Action <IMemcacheTransport> transportAvailable, bool planToConnect, Func <bool> nodeClosing) { if (clientConfig == null) { throw new ArgumentException("Client config should not be null"); } _endPoint = endpoint; _clientConfig = clientConfig; _registerEvents = registerEvents; _transportAvailable = transportAvailable; _nodeClosing = nodeClosing; _connectTimer = new Timer(TryConnect); _initialized = false; _registerEvents(this); _pendingRequests = new ConcurrentQueue <IMemcacheRequest>(); _sendAsynchEvtArgs = new SocketAsyncEventArgs(); _sendAsynchEvtArgs.Completed += OnSendRequestComplete; _receiveHeaderAsynchEvtArgs = new SocketAsyncEventArgs(); _receiveHeaderAsynchEvtArgs.SetBuffer(new byte[MemcacheResponseHeader.Size], 0, MemcacheResponseHeader.Size); _receiveHeaderAsynchEvtArgs.Completed += OnReceiveHeaderComplete; _receiveBodyAsynchEvtArgs = new SocketAsyncEventArgs(); _receiveBodyAsynchEvtArgs.Completed += OnReceiveBodyComplete; if (planToConnect) { _connectTimer.Change(0, Timeout.Infinite); } }
public MemcacheTransportForTest(EndPoint endpoint, MemcacheClientConfiguration clientConfig, Action<IMemcacheTransport> registerEvents, Action<IMemcacheTransport> transportAvailable, bool planToConnect, Func<bool> nodeClosing, Action onCreate, Action onDispose) : base(endpoint, clientConfig, registerEvents, transportAvailable, planToConnect, nodeClosing) { if (onCreate != null) onCreate(); OnDispose = onDispose; }
public MemcacheTransportForTest(EndPoint endpoint, MemcacheClientConfiguration clientConfig, Action <IMemcacheTransport> registerEvents, Action <IMemcacheTransport> transportAvailable, bool planToConnect, Func <bool> nodeClosing, Action onCreate, Action onDispose) : base(endpoint, clientConfig, registerEvents, transportAvailable, planToConnect, nodeClosing) { if (onCreate != null) { onCreate(); } OnDispose = onDispose; }
public void UnsupportedSerializationThrows() { var config = new MemcacheClientConfiguration { ClusterFactory = c => _clusterMock, }; var client = new MemcacheClient(config); // assert that both set and get throws NotSupportedException for an unknown type Assert.Throws(typeof(NotSupportedException), () => client.Set("Hello", new TestType(), TimeSpan.Zero)); Assert.Throws(typeof(NotSupportedException), () => client.Get("Hello", (Status s, TestType v) => { })); }
/// <summary> /// The constructor, see @MemcacheClientConfiguration for details /// </summary> /// <param name="configuration"></param> public MemcacheClient(MemcacheClientConfiguration configuration) { if (configuration == null) { throw new ArgumentException("Client config should not be null"); } _configuration = configuration; _cluster = (configuration.ClusterFactory ?? MemcacheClientConfiguration.DefaultClusterFactory)(configuration); _cluster.NodeAdded += RegisterEvents; _cluster.NodeRemoved += UnregisterEvents; _cluster.Initialize(); }
public MemcacheNode(EndPoint endPoint, MemcacheClientConfiguration configuration) { if (configuration == null) throw new ArgumentException("Client config should not be null"); _configuration = configuration; _endPoint = endPoint; _tokenSource = new CancellationTokenSource(); _transportPool = new BlockingCollection<IMemcacheTransport>(new ConcurrentStack<IMemcacheTransport>()); for (int i = 0; i < configuration.PoolSize; ++i) (configuration.TransportFactory ?? MemcacheTransport.DefaultAllocator) (endPoint, configuration, RegisterEvents, TransportAvailable, false, IsClosing); }
public FakeTransport Factory(EndPoint endPoint, MemcacheClientConfiguration clientConfig, Action <IMemcacheTransport> registerEvents, Action <IMemcacheTransport> transportAvailable, bool autoConnect, Func <bool> nodeClosing) { registerEvents(this); _transportAvailable = transportAvailable; if (transportAvailable != null) { transportAvailable(this); } return(this); }
public void MemcacheTransportDisposeBasicTest(int nbOfNodes, int nbOfTransportsPerNode) { int createdTransports = 0; int disposedTransports = 0; // Memcache client config var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportConnectTimerPeriod = TimeSpan.FromMilliseconds(100), TransportFactory = (_, __, ___, ____, _____, ______) => { return(new MemcacheTransportForTest(_, __, ___, ____, _____, ______, () => { createdTransports++; }, () => { disposedTransports++; })); }, PoolSize = nbOfTransportsPerNode, }; var serverMocks = new List <ServerMock>(nbOfNodes); try { for (int p = 0; p < nbOfNodes; p++) { var serverMock = new ServerMock(); config.NodesEndPoints.Add(serverMock.ListenEndPoint); serverMocks.Add(serverMock); } // Create Memcache client var memcacheClient = new MemcacheClient(config); // Test the number of transports that have been created Assert.AreEqual(nbOfNodes * nbOfTransportsPerNode, createdTransports, "Expected number of transports = number of nodes * poolSize"); // Dispose the client and test that the number of transports is back to zero memcacheClient.Dispose(); Assert.AreEqual(createdTransports, disposedTransports, "Expected all the transports to be disposed"); } finally { foreach (var mock in serverMocks) { mock.Dispose(); } } }
public void SyncNodeDeadDetection() { bool aliveness = true; var transportMocks = new List <TransportMock>(); var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportFactory = (_, __, r, s, ___, ____) => { var transport = new TransportMock(r) { IsAlive = aliveness, Setup = s }; transportMocks.Add(transport); if (s != null) { s(transport); } return(transport); }, PoolSize = 2, }; var node = new MemcacheNode(null, config); CollectionAssert.IsNotEmpty(transportMocks, "No transport has been created by the node"); Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request through the node"); // creation of new transport will set them as dead aliveness = false; foreach (var transport in transportMocks) { transport.IsAlive = false; } Assert.IsFalse(node.TrySend(new NoOpRequest(), 5000), "The node did not failed with all transport deads"); foreach (var transport in transportMocks) { transport.IsAlive = true; } Assert.IsFalse(node.IsDead, "The node is still dead, should be alive now !"); Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request throught the node after it's alive"); }
public void Setup() { _configuration = new MemcacheClientConfiguration { QueueTimeout = 0, NodeFactory = (ipendpoint, config, dispose) => new NodeMock { EndPoint = ipendpoint, DefaultResponse = Status.NoError, }, }; _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 1 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 2 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 3 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 4 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 5 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 6 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 7 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 8 }), 11211)); }
public void TestSetUp() { _counters = new TestCounters(); _mreConfig = new ManualResetEventSlim(); var config = new MemcacheClientConfiguration() { TransportConnectTimerPeriod = Timeout.InfiniteTimeSpan, NodeFactory = (ipendpoint, _) => new NodeMock { EndPoint = ipendpoint, DefaultResponse = Status.NoError }, }; _cluster = new CouchbaseCluster(config, "Some.Bucket", TimeSpan.FromSeconds(30), new[] { new IPEndPoint(0, 0) }); _cluster.NodeAdded += _ => _counters.IncrementNodesAdded(); _cluster.NodeRemoved += _ => _counters.IncrementNodesRemoved(); _cluster.OnError += e => { _counters.IncrementErrors(); Console.Error.WriteLine(e.Message); Console.Error.WriteLine(e.StackTrace); }; _cluster.OnConfig += () => _mreConfig.Set(); }
public void ExecutionContextShouldFlow() { var config = new MemcacheClientConfiguration { ClusterFactory = c => _clusterMock, }; var client = new MemcacheClient(config); object value = null; _extra = new byte[] { 0, 0, 0, 0 }; CallContext.LogicalSetData(CallContextKey, "OK"); client.Get("test", (Status s, byte[] v) => { value = CallContext.LogicalGetData("MemcacheClientTest"); }); Assert.AreEqual("OK", value, "The execution context didn't flow"); }
/// <summary> /// The constructor, see @MemcacheClientConfiguration for details /// </summary> /// <param name="configuration"></param> public MemcacheClient(MemcacheClientConfiguration configuration) { if (configuration == null) { throw new ArgumentException("Client config should not be null"); } OngoingDispose = false; _configuration = configuration; _locator = configuration.NodeLocator ?? MemcacheClientConfiguration.DefaultLocatorFactory(); _nodes = new List <IMemcacheNode>(configuration.NodesEndPoints.Count); foreach (var nodeEndPoint in configuration.NodesEndPoints) { var node = (configuration.NodeFactory ?? MemcacheClientConfiguration.DefaultNodeFactory)(nodeEndPoint, configuration, this); _nodes.Add(node); RegisterEvents(node); } _locator.Initialize(_nodes); }
public void SerializationThrows() { var serializerMoq = new Mock <ISerializer <TestType> >(); serializerMoq.Setup(s => s.FromBytes(It.IsAny <byte[]>())).Throws(new SerializationException()); serializerMoq.Setup(s => s.ToBytes(It.IsAny <TestType>())).Throws(new SerializationException()); serializerMoq.Setup(s => s.TypeFlag).Returns(314); Exception raised = null; var config = new MemcacheClientConfiguration { ClusterFactory = c => _clusterMock, }; var serialiserMoqObj = serializerMoq.Object; config.SetSerializer(serialiserMoqObj); var client = new MemcacheClient(config); client.CallbackError += e => raised = e; // test that the throws of the serializer is synchronously propagated Assert.Throws(typeof(SerializationException), () => client.Set("Hello", new TestType(), TimeSpan.Zero)); // test that the failing serializer does not synchronously throws but sends a CallbackError event _responseHeader = new MemcacheResponseHeader { ExtraLength = 4, TotalBodyLength = 4, Opcode = Opcode.Get, }; _extra = new byte[] { 0, 0, 0, 0 }; Assert.True(client.Get("Hello", (Status s, TestType v) => { })); Assert.IsNotNull(raised); Assert.IsInstanceOf <SerializationException>(raised); }
public void MemcacheTransportDisposeBasicTest(int nbOfNodes, int nbOfTransportsPerNode) { int createdTransports = 0; int disposedTransports = 0; // Memcache client config var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportConnectTimerPeriod = TimeSpan.FromMilliseconds(100), TransportFactory = (_, __, ___, ____, _____, ______) => new MemcacheTransportForTest(_, __, ___, ____, _____, ______, () => { createdTransports++; }, () => { disposedTransports++; }), PoolSize = nbOfTransportsPerNode, }; var serverMocks = new List<ServerMock>(nbOfNodes); try { for (int p = 0; p < nbOfNodes; p++) { var serverMock = new ServerMock(); config.NodesEndPoints.Add(serverMock.ListenEndPoint); serverMocks.Add(serverMock); } // Create Memcache client var memcacheClient = new MemcacheClient(config); // Test the number of transports that have been created Assert.AreEqual(nbOfNodes * nbOfTransportsPerNode, createdTransports, "Expected number of transports = number of nodes * poolSize"); // Dispose the client and test that the number of transports is back to zero memcacheClient.Dispose(); Assert.AreEqual(createdTransports, disposedTransports, "Expected all the transports to be disposed"); } finally { foreach (var mock in serverMocks) { mock.Dispose(); } } }
public void NodeWorkingTransportsTest(int nbOfTransportsPerNode) { int createdTransports = 0; int rememberPort; var mutex = new ManualResetEventSlim(false); Status returnStatus = Status.NoError; MemcacheNode theNode = null; // Memcache client config var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportFactory = (_, __, ___, ____, _____, ______) => new MemcacheTransportForTest(_, __, ___, ____, _____, ______, () => { createdTransports++; }, () => { }), NodeFactory = (_, __) => theNode = MemcacheClientConfiguration.DefaultNodeFactory(_, __) as MemcacheNode, PoolSize = nbOfTransportsPerNode, }; MemcacheClient memcacheClient; using (var serverMock1 = new ServerMock()) { config.NodesEndPoints.Add(serverMock1.ListenEndPoint); rememberPort = serverMock1.ListenEndPoint.Port; serverMock1.ResponseBody = new byte[24]; // Create a Memcache client with one node memcacheClient = new MemcacheClient(config); // Check that we hooked to the MemcacheNode Assert.IsNotNull(theNode, "Did not hook to the MemcacheNode while creating the client"); // Check the number of transports that have been created Assert.AreEqual(nbOfTransportsPerNode, createdTransports, "The number of created should be the number of configured transport"); // Check the number of working transports (meaning, connected to the server) and the node state // By default, the transport are marked as being available upon creation of the client Assert.AreEqual(nbOfTransportsPerNode, theNode.WorkingTransports, "The number of working transport should be the number of created transport (1)"); Assert.IsFalse(theNode.IsDead, "The node should be alive (1)"); Assert.AreEqual(1, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (1)"); // Do a get to initialize one of the transports Assert.IsTrue(memcacheClient.Get("whatever", (Status s, byte[] o) => { returnStatus = s; mutex.Set(); }), "The request should be sent correctly (1)"); Assert.IsTrue(mutex.Wait(1000), "Timeout on the get request"); Assert.AreEqual(Status.InternalError, returnStatus, "The status of the request should be InternalError (1)"); mutex.Reset(); // Check the number of working transports and the node state Assert.AreEqual(nbOfTransportsPerNode, theNode.WorkingTransports, "The number of working transport should be the number of created transport (2)"); Assert.IsFalse(theNode.IsDead, "The node should be alive (2)"); Assert.AreEqual(1, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (2)"); } // Wait for the ServerMock to be fully disposed Thread.Sleep(100); // Attempt to send a request to take one of the transports out of the pool // After that the transport should be dead Assert.IsTrue(memcacheClient.Get("whatever", (Status s, byte[] o) => { returnStatus = s; mutex.Set(); }), "The request should be sent correctly (2)"); Assert.IsTrue(mutex.Wait(1000), "Timeout on the get request"); Assert.AreEqual(Status.InternalError, returnStatus, "The status of the request should be InternalError (2)"); mutex.Reset(); // Check the number of working transports and the node state Assert.AreEqual(nbOfTransportsPerNode - 1, theNode.WorkingTransports, "The number of working transport should be the number of created transport minus 1"); if (nbOfTransportsPerNode == 1) { Assert.IsTrue(theNode.IsDead, "The node should be dead (3)"); Assert.AreEqual(0, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (3)"); } else { Assert.IsFalse(theNode.IsDead, "The node should be alive (3)"); Assert.AreEqual(1, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (3)"); } // A new transport has been allocated and is periodically trying to reconnect Assert.AreEqual(nbOfTransportsPerNode + 1, createdTransports, "Expected a new transport to be created to replace the disposed one"); using (var serverMock2 = new ServerMock(rememberPort)) { serverMock2.ResponseBody = new byte[24]; // After some delay, the transport should connect Assert.That(() => theNode.WorkingTransports, new DelayedConstraint(new EqualConstraint(nbOfTransportsPerNode), 4000, 100), "After a while, the transport should manage to connect"); Assert.IsFalse(theNode.IsDead, "The node should be alive (4)"); Assert.AreEqual(1, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (4)"); // Attempt a get Assert.IsTrue(memcacheClient.Get("whatever", (Status s, byte[] o) => { returnStatus = s; mutex.Set(); }), "The request should be sent correctly (4)"); Assert.IsTrue(mutex.Wait(1000), "Timeout on the get request"); Assert.AreEqual(Status.InternalError, returnStatus, "The status of the request should be InternalError (4)"); mutex.Reset(); // Check the number of working transports and the node state Assert.AreEqual(nbOfTransportsPerNode, theNode.WorkingTransports, "The number of working transport should be the number of created transport (5)"); Assert.IsFalse(theNode.IsDead, "The node should be alive (5)"); Assert.AreEqual(1, memcacheClient.AliveNodes, "Number of alive nodes is incorrect (5)"); // Dispose the client memcacheClient.Dispose(); } }
public void ReceiveFailConsistency([Values(true, false)] bool failInBody) { using (var serverMock = new ServerMock()) { var endpoint = serverMock.ListenEndPoint; serverMock.ResponseBody = new byte[24]; if (failInBody) { // the Opaque vs RequestId is check in the body receiving callback // I put 2 different values new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, Opaque = 0, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); } else { // the magic number is checked in the header receiving callback // I corrupt it new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, Opaque = 1, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); serverMock.ResponseHeader.CopyFrom(0, (uint)42); } var config = new MemcacheClientConfiguration { PoolSize = 1, }; var node = new Memcache.Node.MemcacheNode(endpoint, config); var errorMutex = new ManualResetEventSlim(false); var callbackMutex = new ManualResetEventSlim(false); Exception expectedException = null; node.TransportError += e => { Interlocked.Exchange <Exception>(ref expectedException, e); errorMutex.Set(); }; int nodeAliveCount = 0; node.NodeAlive += t => ++ nodeAliveCount; int nodeDeadCount = 0; node.NodeDead += t => ++ nodeDeadCount; Status receivedStatus = Status.NoError; node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // must wait before the next test because the TransportError event is fired after the callback call // Expected result : // * The callback must have been called before 1 sec // * The failure callback must have been called, so the received status must be InternalError // * An MemcacheException should have been raised by the receive due to the bad response // * The node should have exaclty one transport in its pool // more mean that we added it twice after a failure, less means we didn't putted it back in the pool // * The node should not be seen has dead yet Assert.IsTrue(callbackMutex.Wait(1000), @"The 1st callback has not been received after 1 second"); Assert.IsTrue(errorMutex.Wait(1000), @"The 1st error has not been received after 1 second"); Assert.AreEqual(Status.InternalError, receivedStatus, @"A bad response has not sent an InternalError to the request callback"); Assert.IsInstanceOf <Memcache.Exceptions.MemcacheException>(expectedException, @"A bad response has not triggered a transport error. Expected a MemcacheException."); Assert.AreEqual(0, nodeDeadCount, @"The node has been detected has dead before a new send has been made"); // After a short delay, the transport should be back in the transport pool (node.PoolSize == 1) Assert.That(() => node.PoolSize, new DelayedConstraint(new EqualConstraint(1), 2000, 100), "After a while, the transport should be back in the pool"); new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, // must be the same or it will crash : TODO add a test to ensure we detect this fail Opaque = 1, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); serverMock.ResponseBody = null; expectedException = null; callbackMutex.Reset(); receivedStatus = Status.NoError; var result = node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // Expected result : // * An SocketException should have been raised by the send, since the previous receice has disconnected the socket // * The return must be true, because the request have been enqueued before the transport seen the socket died // * The failure callback must have been called, so the received status must be InternalError Assert.IsTrue(callbackMutex.Wait(1000), @"The 2nd callback has not been received after 1 second"); Assert.IsTrue(result, @"The first failed request should not see a false return"); Assert.AreEqual(Status.InternalError, receivedStatus, @"The send operation should have detected that the socket is dead"); // After a short delay, the transport should connect Assert.That(() => node.PoolSize, new DelayedConstraint(new EqualConstraint(1), 2000, 100), "After a while, the transport should manage to connect"); expectedException = null; callbackMutex.Reset(); result = node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // Expected result : everything should works fine now // * The return must be true, because the new transport should be available now // * No exception should have been raised // * The callback must have been called and with a NoError status Assert.IsTrue(result, @"The node has not been able to send a new request after a disconnection"); Assert.IsTrue(callbackMutex.Wait(1000), @"The message has not been received after 1 second, case after reconnection"); Assert.AreEqual(Status.NoError, receivedStatus, @"The response after a reconnection is still not NoError"); Assert.IsNull(expectedException, "The request shouldn't have thrown an exception"); } }
public void ReceiveFailConsistency([Values(true, false)] bool failInBody) { using (var serverMock = new ServerMock()) { var endpoint = serverMock.ListenEndPoint; serverMock.ResponseBody = new byte[24]; if (failInBody) { // the Opaque vs RequestId is check in the body receiving callback // I put 2 different values new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, Opaque = 0, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); } else { // the magic number is checked in the header receiving callback // I corrupt it new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, Opaque = 1, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); serverMock.ResponseHeader.CopyFrom(0, (uint)42); } var config = new MemcacheClientConfiguration { PoolSize = 1, }; var node = new Memcache.Node.MemcacheNode(endpoint, config); var errorMutex = new ManualResetEventSlim(false); var callbackMutex = new ManualResetEventSlim(false); Exception expectedException = null; node.TransportError += e => { Interlocked.Exchange<Exception>(ref expectedException, e); errorMutex.Set(); }; int nodeAliveCount = 0; node.NodeAlive += t => ++nodeAliveCount; int nodeDeadCount = 0; node.NodeDead += t => ++nodeDeadCount; Status receivedStatus = Status.NoError; node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // must wait before the next test because the TransportError event is fired after the callback call // Expected result : // * The callback must have been called before 1 sec // * The failure callback must have been called, so the received status must be InternalError // * An MemcacheException should have been raised by the receive due to the bad response // * The node should have exaclty one transport in its pool // more mean that we added it twice after a failure, less means we didn't putted it back in the pool // * The node should not be seen has dead yet Assert.IsTrue(callbackMutex.Wait(1000), @"The 1st callback has not been received after 1 second"); Assert.IsTrue(errorMutex.Wait(1000), @"The 1st error has not been received after 1 second"); Assert.AreEqual(Status.InternalError, receivedStatus, @"A bad response has not sent an InternalError to the request callback"); Assert.IsInstanceOf<Memcache.Exceptions.MemcacheException>(expectedException, @"A bad response has not triggered a transport error. Expected a MemcacheException."); Assert.AreEqual(0, nodeDeadCount, @"The node has been detected has dead before a new send has been made"); // After a short delay, the transport should be back in the transport pool (node.PoolSize == 1) Assert.That(() => node.PoolSize, new DelayedConstraint(new EqualConstraint(1), 2000, 100), "After a while, the transport should be back in the pool"); new MemcacheResponseHeader { Cas = 8, DataType = 12, ExtraLength = 0, KeyLength = 0, // must be the same or it will crash : TODO add a test to ensure we detect this fail Opaque = 1, Status = Status.NoError, Opcode = Opcode.Set, TotalBodyLength = 0, }.ToData(serverMock.ResponseHeader); serverMock.ResponseBody = null; expectedException = null; callbackMutex.Reset(); receivedStatus = Status.NoError; var result = node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // Expected result : // * An SocketException should have been raised by the send, since the previous receice has disconnected the socket // * The return must be true, because the request have been enqueued before the transport seen the socket died // * The failure callback must have been called, so the received status must be InternalError Assert.IsTrue(callbackMutex.Wait(1000), @"The 2nd callback has not been received after 1 second"); Assert.IsTrue(result, @"The first failed request should not see a false return"); Assert.AreEqual(Status.InternalError, receivedStatus, @"The send operation should have detected that the socket is dead"); // After a short delay, the transport should connect Assert.That(() => node.PoolSize, new DelayedConstraint(new EqualConstraint(1), 2000, 100), "After a while, the transport should manage to connect"); expectedException = null; callbackMutex.Reset(); result = node.TrySend( new SetRequest { RequestOpcode = Opcode.Set, RequestId = 1, Expire = TimeSpan.FromSeconds(1), Key = "Key".Select(c => (byte)c).ToArray(), Message = new byte[] { 0, 1, 2, 3, 4 }, CallBack = s => { receivedStatus = s; callbackMutex.Set(); }, }, 1000); // Expected result : everything should works fine now // * The return must be true, because the new transport should be available now // * No exception should have been raised // * The callback must have been called and with a NoError status Assert.IsTrue(result, @"The node has not been able to send a new request after a disconnection"); Assert.IsTrue(callbackMutex.Wait(1000), @"The message has not been received after 1 second, case after reconnection"); Assert.AreEqual(Status.NoError, receivedStatus, @"The response after a reconnection is still not NoError"); Assert.IsNull(expectedException, "The request shouldn't have thrown an exception"); } }
public void MemcacheTransportDisposeTransportNotInPoolTest() { int createdTransports = 0; int disposedTransports = 0; var mutex1 = new ManualResetEventSlim(false); var mutex2 = new ManualResetEventSlim(false); Status returnStatus = Status.NoError; // Memcache client config var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportConnectTimerPeriod = TimeSpan.FromMilliseconds(100), TransportFactory = (_, __, ___, ____, _____, ______) => new MemcacheTransportForTest(_, __, ___, ____, _____, ______, () => { createdTransports++; }, () => { disposedTransports++; mutex1.Set(); }), PoolSize = 1, }; MemcacheClient memcacheClient; using (var serverMock = new ServerMock()) { config.NodesEndPoints.Add(serverMock.ListenEndPoint); serverMock.ResponseBody = new byte[24]; // Create Memcache client memcacheClient = new MemcacheClient(config); // Test the number of transports that have been created Assert.AreEqual(1, createdTransports); // Do a get to initialize the transport Assert.IsTrue(memcacheClient.Get("whatever", (Status s, byte[] o) => { returnStatus = s; mutex2.Set(); })); Assert.IsTrue(mutex2.Wait(1000), "Timeout on the get request"); Assert.AreEqual(Status.InternalError, returnStatus); mutex2.Reset(); } // Wait for the ServerMock to be fully disposed Thread.Sleep(100); // Attempt to send a request to take the transport out of the pool Assert.IsTrue(memcacheClient.Get("whatever", (Status s, byte[] o) => { returnStatus = s; mutex2.Set(); })); Assert.IsTrue(mutex2.Wait(1000), "Timeout on the get request"); Assert.AreEqual(Status.InternalError, returnStatus); mutex2.Reset(); // The initial transport should now be disposed, a new transport has been allocated and // is periodically trying to reconnect Assert.IsTrue(mutex1.Wait(1000), "Timeout on initial transport disposal"); Assert.AreEqual(1, disposedTransports, "Expected the initial transport to be disposed"); Assert.AreEqual(2, createdTransports, "Expected a new transport to be created to replace the disposed one"); mutex1.Reset(); // Dispose the client memcacheClient.Dispose(); // Wait enough time for the reconnect timer to fire at least once Assert.IsTrue(mutex1.Wait(4000), "MemcacheTransport was not disposed before the timeout"); // Check that all transports have been disposed Assert.AreEqual(2, disposedTransports); Assert.AreEqual(createdTransports, disposedTransports); }
public void SyncNodeDeadDetection() { bool aliveness = true; var transportMocks = new List<TransportMock>(); var config = new MemcacheClientConfiguration { DeadTimeout = TimeSpan.FromSeconds(1), TransportFactory = (_, __, r, s, ___, ____) => { var transport = new TransportMock(r) { IsAlive = aliveness, Setup = s }; transportMocks.Add(transport); if (s != null) s(transport); return transport; }, PoolSize = 2, }; var node = new MemcacheNode(null, config); CollectionAssert.IsNotEmpty(transportMocks, "No transport has been created by the node"); Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request through the node"); // creation of new transport will set them as dead aliveness = false; foreach (var transport in transportMocks) transport.IsAlive = false; Assert.IsFalse(node.TrySend(new NoOpRequest(), 5000), "The node did not failed with all transport deads"); foreach (var transport in transportMocks) transport.IsAlive = true; Assert.IsFalse(node.IsDead, "The node is still dead, should be alive now !"); Assert.IsTrue(node.TrySend(new NoOpRequest(), 5000), "Unable to send a request throught the node after it's alive"); }
public FakeTransport Factory(EndPoint endPoint, MemcacheClientConfiguration clientConfig, Action<IMemcacheTransport> registerEvents, Action<IMemcacheTransport> transportAvailable, bool autoConnect, Func<bool> nodeClosing) { registerEvents(this); _transportAvailable = transportAvailable; if (transportAvailable != null) transportAvailable(this); return this; }
public StaticCluster(MemcacheClientConfiguration configuration) { _configuration = configuration; }
public void Setup() { _configuration = new MemcacheClientConfiguration { QueueTimeout = 0, NodeFactory = (ipendpoint, config) => new NodeMock { EndPoint = ipendpoint, DefaultResponse = Status.NoError, }, }; _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 1 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 2 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 3 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 4 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 5 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 6 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 7 }), 11211)); _configuration.NodesEndPoints.Add(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 18, 8 }), 11211)); }