<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>performance.txt</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,9 @@
+= 1.7.1
+
+* Performance optimization.  Rely on higher performance operating system socket timeouts for 
+  low-level socket read/writes where possible, instead of the (slower) SystemTimer or (slowest,
+  unreliable) Timeout libraries.
+
 = 1.7.0 (2009-03-08)
 
 * Go through the memcached protocol document and implement any commands not already implemented:</diff>
      <filename>History.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -33,7 +33,7 @@ class MemCache
   ##
   # The version of MemCache you are using.
 
-  VERSION = '1.7.0'
+  VERSION = '1.7.1'
 
   ##
   # Default options for the cache object.
@@ -822,7 +822,7 @@ class MemCache
 
       block.call(socket)
 
-    rescue SocketError, Timeout::Error =&gt; err
+    rescue SocketError, Errno::EAGAIN, Timeout::Error =&gt; err
       logger.warn { &quot;Socket failure: #{err.message}&quot; } if logger
       server.mark_dead(err)
       handle_error(server, err)
@@ -922,13 +922,6 @@ class MemCache
   class Server
 
     ##
-    # The amount of time to wait to establish a connection with a memcached
-    # server.  If a connection cannot be established within this time limit,
-    # the server will be marked as down.
-
-    CONNECT_TIMEOUT = 0.25
-
-    ##
     # The amount of time to wait before attempting to re-establish a
     # connection with a server that is marked dead.
 
@@ -1011,14 +1004,11 @@ class MemCache
 
       # Attempt to connect if not already connected.
       begin
-        @sock = @timeout ? TCPTimeoutSocket.new(@host, @port, @timeout) : TCPSocket.new(@host, @port)
-
-        if Socket.constants.include? 'TCP_NODELAY' then
-          @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
-        end
+        @sock = connect_to(@host, @port, @timeout)
+        @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
         @retry  = nil
         @status = 'CONNECTED'
-      rescue SocketError, SystemCallError, IOError, Timeout::Error =&gt; err
+      rescue SocketError, SystemCallError, IOError =&gt; err
         logger.warn { &quot;Unable to open socket: #{err.class.name}, #{err.message}&quot; } if logger
         mark_dead err
       end
@@ -1026,6 +1016,33 @@ class MemCache
       return @sock
     end
 
+    def connect_to(host, port, timeout=nil)
+      addr = Socket.getaddrinfo(host, nil)
+      sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
+
+      if timeout
+        secs = Integer(timeout)
+        usecs = Integer((timeout - secs) * 1_000_000)
+        optval = [secs, usecs].pack(&quot;l_2&quot;)
+        sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
+        sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
+
+        # Socket timeouts don't work for more complex IO operations
+        # like gets which lay on top of read.  We need to fall back to
+        # the standard Timeout mechanism.
+        sock.instance_eval &lt;&lt;-EOR
+          alias :blocking_gets :gets
+          def gets
+            MemCacheTimer.timeout(#{timeout}) do
+              self.blocking_gets
+            end
+          end
+        EOR
+      end
+      sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
+      sock
+    end
+
     ##
     # Close the connection to the memcached server targeted by this
     # object.  The server is not considered dead.
@@ -1059,51 +1076,6 @@ class MemCache
 
 end
 
-# TCPSocket facade class which implements timeouts.
-class TCPTimeoutSocket
-  
-  def initialize(host, port, timeout)
-    MemCacheTimer.timeout(MemCache::Server::CONNECT_TIMEOUT) do
-      @sock = TCPSocket.new(host, port)
-      @len = timeout
-    end
-  end
-  
-  def write(*args)
-    MemCacheTimer.timeout(@len) do
-      @sock.write(*args)
-    end
-  end
-  
-  def gets(*args)
-    MemCacheTimer.timeout(@len) do
-      @sock.gets(*args)
-    end
-  end
-  
-  def read(*args)
-    MemCacheTimer.timeout(@len) do
-      @sock.read(*args)
-    end
-  end
-  
-  def _socket
-    @sock
-  end
-  
-  def method_missing(meth, *args)
-    @sock.__send__(meth, *args)
-  end
-  
-  def closed?
-    @sock.closed?
-  end
-  
-  def close
-    @sock.close
-  end
-end
-
 module Continuum
   POINTS_PER_SERVER = 160 # this is the default in libmemcached
 </diff>
      <filename>lib/memcache.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 HERE = File.dirname(__FILE__)
-$LOAD_PATH &lt;&lt; &quot;#{HERE}/../lib/&quot;
+$LOAD_PATH.unshift &quot;#{HERE}/../lib&quot;
 #$LOAD_PATH &lt;&lt; &quot;/Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/vendor/memcache-client-1.5.1&quot;
 
 require 'benchmark'</diff>
      <filename>test/test_benchmark.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>cd4b9982e20b1003e81112e3234128dc4db3b11a</id>
    </parent>
  </parents>
  <author>
    <name>Mike Perham</name>
    <email>mperham@gmail.com</email>
  </author>
  <url>http://github.com/mperham/memcache-client/commit/9f5201b77ccb6ef0d021e741cab8468151f2535d</url>
  <id>9f5201b77ccb6ef0d021e741cab8468151f2535d</id>
  <committed-date>2009-03-15T18:39:11-07:00</committed-date>
  <authored-date>2009-03-15T18:39:11-07:00</authored-date>
  <message>Optimization: use raw socket timeouts where possible.</message>
  <tree>7539e489ad83b87bb8d212f15956bdd513fc0856</tree>
  <committer>
    <name>Mike Perham</name>
    <email>mperham@gmail.com</email>
  </committer>
</commit>
