lib/memcache.rb in memcache-client-1.1.0 vs lib/memcache.rb in memcache-client-1.2.0

- old
+ new

@@ -3,10 +3,35 @@ require 'socket' require 'thread' require 'timeout' require 'rubygems' +class String + + ## + # Uses the ITU-T polynomial in the CRC32 algorithm. + + def crc32_ITU_T + n = length + r = 0xFFFFFFFF + + n.times do |i| + r ^= self[i] + 8.times do + if (r & 1) != 0 then + r = (r>>1) ^ 0xEDB88320 + else + r >>= 1 + end + end + end + + r ^ 0xFFFFFFFF + end + +end + ## # A Ruby client library for memcached. # # This is intended to provide access to basic memcached functionality. It # does not attempt to be complete implementation of the entire API. @@ -168,14 +193,25 @@ new_err.set_backtrace err.backtrace raise new_err end ## + # Retrieves +keys+ and returns a Hash mapping keys to values. + + def get_multi(*keys) + values = {} + keys.flatten.each { |key| values[key] = get key } + values + end + + ## # Add +key+ to the cache with value +value+ that expires in +expiry+ # seconds. def set(key, value, expiry = 0) + @mutex.lock if @multithread + raise MemCacheError, "No active servers" unless self.active? raise MemCacheError, "Update of readonly cache" if @readonly cache_key = make_cache_key(key) server = get_server_for_key(cache_key) @@ -184,40 +220,42 @@ marshaled_value = Marshal.dump value command = "set #{cache_key} 0 #{expiry} #{marshaled_value.size}\r\n#{marshaled_value}\r\n" begin - @mutex.synchronize do - sock.write command - sock.gets - end + sock.write command + sock.gets rescue SystemCallError, IOError => err server.close raise MemCacheError, err.message end + ensure + @mutex.unlock if @multithread end ## # Removes +key+ from the cache in +expiry+ seconds. def delete(key, expiry = 0) + @mutex.lock if @multithread + raise MemCacheError, "No active servers" unless active? cache_key = make_cache_key key server = get_server_for_key cache_key sock = server.socket raise MemCacheError, "No connection to server" if sock.nil? begin - @mutex.synchronize do - sock.write "delete #{cache_key} #{expiry}\r\n" - sock.gets - end + sock.write "delete #{cache_key} #{expiry}\r\n" + sock.gets rescue SystemCallError, IOError => err server.close raise MemCacheError, err.message end + ensure + @mutex.unlock if @multithread end ## # Reset the connection to all memcache servers. This should be called if # there is a problem with a cache lookup that might have left the connection @@ -259,23 +297,30 @@ def get_server_for_key(key) raise MemCacheError, "No servers available" if @servers.empty? return @servers.first if @servers.length == 1 - # Hash the value of the key to select the bucket. - hkey = key.hash + hkey = hash_for key - # Fetch a server for the given key, retrying if that server is offline. 20.times do |try| - server = @buckets[(hkey + try) % @buckets.nitems] + server = @buckets[hkey % @buckets.nitems] return server if server.alive? + hkey += hash_for "#{try}#{key}" end raise MemCacheError, "No servers available" end ## + # Returns an interoperable hash value for +key+. (I think, docs are + # sketchy for down servers). + + def hash_for(key) + (key.crc32_ITU_T >> 16) & 0x7fff + end + + ## # Fetches the raw data for +cache_key+ from +server+. Returns nil on cache # miss. def cache_get(server, cache_key) socket = server.socket @@ -352,10 +397,11 @@ @host = host @port = port.to_i @weight = weight.to_i @multithread = @memcache.multithread + @mutex = Mutex.new @sock = nil @retry = nil @status = 'NOT CONNECTED' end @@ -396,10 +442,10 @@ @sock = timeout CONNECT_TIMEOUT do TCPSocket.new @host, @port end @retry = nil @status = 'CONNECTED' - rescue SystemCallError, IOError, Timeout::Error => err + rescue SocketError, SystemCallError, IOError, Timeout::Error => err mark_dead err.message end return @sock ensure