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