/// <summary> /// Returns a socket to the pool. /// If the socket is dead, it will be destroyed. /// If there are more than MaxPoolSize sockets in the pool, it will be destroyed. /// If there are less than MinPoolSize sockets in the pool, it will always be put back. /// If there are something inbetween those values, the age of the socket is checked. /// If it is older than the SocketRecycleAge, it is destroyed, otherwise it will be /// put back in the pool. /// </summary> internal void Return(PooledSocket socket) { //If the socket is dead, destroy it. if (!socket.IsAlive) { Interlocked.Increment(ref deadsocketsonreturn); socket.Close(); } else { //Clean up socket if (socket.Reset()) { Interlocked.Increment(ref dirtysocketsonreturn); } //Check pool size. if (queue.Count >= owner.MaxPoolSize) { //If the pool is full, destroy the socket. socket.Close(); } else if (queue.Count > owner.MinPoolSize && DateTime.Now - socket.Created > owner.SocketRecycleAge) { //If we have more than the minimum amount of sockets, but less than the max, and the socket is older than the recycle age, we destroy it. socket.Close(); } else { //Put the socket back in the pool. lock (queue) { queue.Enqueue(socket); } } } }
//Private method for reading results of the "get" command. private bool readValue(PooledSocket socket, out object value, out string key, out ulong unique) { string response = socket.ReadResponse(); string[] parts = response.Split(' '); //Result line from server: "VALUE <key> <flags> <bytes> <cas unique>" if (parts[0] == "VALUE") { key = parts[1]; SerializedType type = (SerializedType)Enum.Parse(typeof(SerializedType), parts[2]); byte[] bytes = new byte[Convert.ToUInt32(parts[3], CultureInfo.InvariantCulture)]; if (parts.Length > 4) { unique = Convert.ToUInt64(parts[4]); } else { unique = 0; } socket.Read(bytes); socket.SkipUntilEndOfLine(); //Skip the trailing \r\n try { value = Serializer.DeSerialize(bytes, type); } catch (Exception e) { //If deserialization fails, return null value = null; logger.Error("Error deserializing object for key '" + key + "' of type " + type + ".", e); } return true; } else { key = null; value = null; unique = 0; return false; } }
/// <summary> /// Gets a socket from the pool. /// If there are no free sockets, a new one will be created. If something goes /// wrong while creating the new socket, this pool's endpoint will be marked as dead /// and all subsequent calls to this method will return null until the retry interval /// has passed. /// </summary> internal PooledSocket Acquire() { //Do we have free sockets in the pool? //if so - return the first working one. //if not - create a new one. Interlocked.Increment(ref acquired); lock (queue) { while (queue.Count > 0) { PooledSocket socket = queue.Dequeue(); if (socket != null && socket.IsAlive) { Interlocked.Increment(ref reusedsockets); return socket; } Interlocked.Increment(ref deadsocketsinpool); } } Interlocked.Increment(ref newsockets); //If we know the endpoint is dead, check if it is time for a retry, otherwise return null. if (isEndPointDead) { if (DateTime.Now > deadEndPointRetryTime) { //Retry isEndPointDead = false; } else { //Still dead return null; } } //Try to create a new socket. On failure, mark endpoint as dead and return null. try { PooledSocket socket = new PooledSocket(this, endPoint, owner.SendReceiveTimeout, owner.ConnectTimeout); //Reset retry timer on success. deadEndPointSecondsUntilRetry = 1; return socket; } catch (Exception e) { Interlocked.Increment(ref failednewsockets); logger.Error("Error connecting to: " + endPoint.Address, e); //Mark endpoint as dead isEndPointDead = true; //Retry in 2 minutes deadEndPointRetryTime = DateTime.Now.AddSeconds(deadEndPointSecondsUntilRetry); if (deadEndPointSecondsUntilRetry < maxDeadEndPointSecondsUntilRetry) { deadEndPointSecondsUntilRetry = deadEndPointSecondsUntilRetry * 2; //Double retry interval until next time } return null; } }