lib/memcache.rb in memcache-client-1.7.5 vs lib/memcache.rb in memcache-client-1.7.6

- old
+ new

@@ -4,20 +4,43 @@ require 'thread' require 'zlib' require 'digest/sha1' require 'net/protocol' +begin + # Try to use the SystemTimer gem instead of Ruby's timeout library + # when running on something that looks like Ruby 1.8.x. See: + # http://ph7spot.com/articles/system_timer + # We don't want to bother trying to load SystemTimer on jruby and + # ruby 1.9+ + if defined?(JRUBY_VERSION) || (RUBY_VERSION >= '1.9') + require 'timeout' + MemCacheTimer = Timeout + else + require 'system_timer' + MemCacheTimer = SystemTimer + end +rescue LoadError => e + puts "[memcache-client] Could not load SystemTimer gem, falling back to Ruby's slower/unsafe timeout library: #{e.message}" + require 'timeout' + MemCacheTimer = Timeout +end + + ## # A Ruby client library for memcached. # class MemCache ## # The version of MemCache you are using. - VERSION = '1.7.5' + VERSION = begin + config = YAML.load(File.read(File.dirname(__FILE__) + '/../VERSION.yml')) + "#{config[:major]}.#{config[:minor]}.#{config[:patch]}" + end ## # Default options for the cache object. DEFAULT_OPTIONS = { @@ -27,11 +50,12 @@ :failover => true, :timeout => 0.5, :logger => nil, :no_reply => false, :check_size => true, - :autofix_keys => false + :autofix_keys => false, + :namespace_separator => ':', } ## # Default memcached port. @@ -144,10 +168,11 @@ @timeout = opts[:timeout] @failover = opts[:failover] @logger = opts[:logger] @no_reply = opts[:no_reply] @check_size = opts[:check_size] + @namespace_separator = opts[:namespace_separator] @mutex = Mutex.new if @multithread logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger Thread.current[:memcache_client] = self.object_id if !@multithread @@ -646,11 +671,11 @@ end if namespace.nil? then key else - "#{@namespace}:#{key}" + "#{@namespace}#{@namespace_separator}#{key}" end end ## # Returns an interoperable hash value for +key+. (I think, docs are @@ -864,11 +889,11 @@ ## # Handles +error+ from +server+. def handle_error(server, error) raise error if error.is_a?(MemCacheError) - server.close if server + server.close if server && server.status == "CONNECTED" new_error = MemCacheError.new error.message new_error.set_backtrace error.backtrace raise new_error end @@ -1014,20 +1039,44 @@ begin @sock = connect_to(@host, @port, @timeout) @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 @retry = nil @status = 'CONNECTED' - rescue SocketError, SystemCallError, IOError => err + rescue SocketError, SystemCallError, IOError, Timeout::Error => err logger.warn { "Unable to open socket: #{err.class.name}, #{err.message}" } if logger mark_dead err end return @sock end def connect_to(host, port, timeout=nil) - io = MemCache::BufferedIO.new(TCPSocket.new(host, port)) + sock = nil + if timeout + MemCacheTimer.timeout(timeout) do + sock = TCPSocket.new(host, port) + end + else + sock = TCPSocket.new(host, port) + end + + io = MemCache::BufferedIO.new(sock) io.read_timeout = timeout + # Getting reports from several customers, including 37signals, + # that the non-blocking timeouts in 1.7.5 don't seem to be reliable. + # It can't hurt to set the underlying socket timeout also, if possible. + if timeout + secs = Integer(timeout) + usecs = Integer((timeout - secs) * 1_000_000) + optval = [secs, usecs].pack("l_2") + begin + io.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval + io.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval + rescue Exception => ex + # Solaris, for one, does not like/support socket timeouts. + @logger.info "[memcache-client] Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}" if @logger + end + end io end ## # Close the connection to the memcached server targeted by this