mirror of
https://github.com/fluencelabs/redis
synced 2025-04-04 16:51:12 +00:00
client libraries synched in git
This commit is contained in:
parent
e083d75262
commit
d7fc9edb18
@ -1,3 +1,6 @@
|
|||||||
|
2009-05-26 ignore gcc warning about write() return code not checked. It is esplicitily this way since the "max number of clients reached" is a best-effort error
|
||||||
|
2009-05-26 max bytes of a received command enlarged from 1k to 16k
|
||||||
|
2009-05-26 RubyRedis: set TCP_NODELAY TCP socket option to to disable the neagle algorithm. Makes a huge difference under some OS, notably Linux
|
||||||
2009-05-25 maxclients implemented, see redis.conf for details
|
2009-05-25 maxclients implemented, see redis.conf for details
|
||||||
2009-05-25 INFO command now reports replication info
|
2009-05-25 INFO command now reports replication info
|
||||||
2009-05-25 minor fix to RubyRedis about bulk commands sent without arguments
|
2009-05-25 minor fix to RubyRedis about bulk commands sent without arguments
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
+ finish command implementations
|
command handlers:
|
||||||
= finish unit tests
|
- support DEL as vararg
|
||||||
Only a few left, to test the SORT command's edge cases (e.g. BY pattern)
|
- support MLLEN and MSCARD
|
||||||
+ determine if we should not use bool return values and instead throw redis_error. (latter).
|
|
||||||
+ maybe more fine-grained exceptions (not just redis_error but operation_not_permitted_error, etc.)
|
unit tests:
|
||||||
- benchmarking
|
- sort with limit
|
||||||
|
- sort lexicographically
|
||||||
|
- sort with pattern and weights
|
||||||
|
|
||||||
|
extras:
|
||||||
|
- benchmarking "test" app
|
||||||
- consistent hashing?
|
- consistent hashing?
|
||||||
- make all string literals constants so they can be easily changed (minor)
|
|
||||||
|
maybe/someday:
|
||||||
|
- make all string literals constants so they can be easily changed
|
||||||
- add conveniences that store a std::set in its entirety (same for std::list, std::vector)
|
- add conveniences that store a std::set in its entirety (same for std::list, std::vector)
|
||||||
|
@ -663,16 +663,24 @@ namespace redis
|
|||||||
const client::string_type & by_pattern,
|
const client::string_type & by_pattern,
|
||||||
client::int_type limit_start,
|
client::int_type limit_start,
|
||||||
client::int_type limit_end,
|
client::int_type limit_end,
|
||||||
const client::string_type & get_pattern,
|
const client::string_vector & get_patterns,
|
||||||
client::sort_order order,
|
client::sort_order order,
|
||||||
bool lexicographically)
|
bool lexicographically)
|
||||||
{
|
{
|
||||||
send_(makecmd("SORT") << key
|
makecmd m("SORT");
|
||||||
|
|
||||||
|
m << key
|
||||||
<< " BY " << by_pattern
|
<< " BY " << by_pattern
|
||||||
<< " LIMIT " << limit_start << ' ' << limit_end
|
<< " LIMIT " << limit_start << ' ' << limit_end;
|
||||||
<< " GET " << get_pattern
|
|
||||||
<< (order == sort_order_ascending ? " ASC" : " DESC")
|
client::string_vector::const_iterator it = get_patterns.begin();
|
||||||
<< (lexicographically ? " ALPHA" : ""));
|
for ( ; it != get_patterns.end(); ++it)
|
||||||
|
m << " GET " << *it;
|
||||||
|
|
||||||
|
m << (order == sort_order_ascending ? " ASC" : " DESC")
|
||||||
|
<< (lexicographically ? " ALPHA" : "");
|
||||||
|
|
||||||
|
send_(m);
|
||||||
|
|
||||||
return recv_multi_bulk_reply_(out);
|
return recv_multi_bulk_reply_(out);
|
||||||
}
|
}
|
||||||
@ -681,7 +689,7 @@ namespace redis
|
|||||||
{
|
{
|
||||||
send_(makecmd("SAVE", true));
|
send_(makecmd("SAVE", true));
|
||||||
recv_ok_reply_();
|
recv_ok_reply_();
|
||||||
e.g. }
|
}
|
||||||
|
|
||||||
void client::bgsave()
|
void client::bgsave()
|
||||||
{
|
{
|
||||||
|
@ -421,7 +421,7 @@ namespace redis
|
|||||||
const string_type & by_pattern,
|
const string_type & by_pattern,
|
||||||
int_type limit_start,
|
int_type limit_start,
|
||||||
int_type limit_end,
|
int_type limit_end,
|
||||||
const string_type & get_pattern,
|
const string_vector & get_patterns,
|
||||||
sort_order order = sort_order_ascending,
|
sort_order order = sort_order_ascending,
|
||||||
bool lexicographically = false);
|
bool lexicographically = false);
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ redis_commands = {
|
|||||||
function(client, command)
|
function(client, command)
|
||||||
-- let's fire and forget! the connection is closed as soon
|
-- let's fire and forget! the connection is closed as soon
|
||||||
-- as the SHUTDOWN command is received by the server.
|
-- as the SHUTDOWN command is received by the server.
|
||||||
network.write(command .. protocol.newline)
|
network.write(client, command .. protocol.newline)
|
||||||
end
|
end
|
||||||
),
|
),
|
||||||
|
|
||||||
|
1
client-libraries/ruby/.gitignore
vendored
1
client-libraries/ruby/.gitignore
vendored
@ -2,3 +2,4 @@ nohup.out
|
|||||||
redis/*
|
redis/*
|
||||||
rdsrv
|
rdsrv
|
||||||
pkg/*
|
pkg/*
|
||||||
|
.idea
|
||||||
|
@ -12,7 +12,10 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
1. redis -
|
1. rspec -
|
||||||
|
sudo gem install rspec
|
||||||
|
|
||||||
|
2. redis -
|
||||||
|
|
||||||
rake redis:install
|
rake redis:install
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
|
|||||||
|
|
||||||
rake dtach:install
|
rake dtach:install
|
||||||
|
|
||||||
3. svn - git is the new black, but we need it for the google codes.
|
3. git - git is the new black.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -8,10 +8,10 @@ require 'tasks/redis.tasks'
|
|||||||
|
|
||||||
GEM = 'redis'
|
GEM = 'redis'
|
||||||
GEM_NAME = 'redis'
|
GEM_NAME = 'redis'
|
||||||
GEM_VERSION = '0.0.3.3'
|
GEM_VERSION = '0.0.3.4'
|
||||||
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark']
|
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark']
|
||||||
EMAIL = "matt.clark@punchstock.com"
|
EMAIL = "ez@engineyard.com"
|
||||||
HOMEPAGE = "http://github.com/winescout/redis-rb"
|
HOMEPAGE = "http://github.com/ezmobius/redis-rb"
|
||||||
SUMMARY = "Ruby client library for redis key value storage server"
|
SUMMARY = "Ruby client library for redis key value storage server"
|
||||||
|
|
||||||
spec = Gem::Specification.new do |s|
|
spec = Gem::Specification.new do |s|
|
||||||
@ -25,10 +25,7 @@ spec = Gem::Specification.new do |s|
|
|||||||
s.authors = AUTHORS
|
s.authors = AUTHORS
|
||||||
s.email = EMAIL
|
s.email = EMAIL
|
||||||
s.homepage = HOMEPAGE
|
s.homepage = HOMEPAGE
|
||||||
|
s.add_dependency "rspec"
|
||||||
# Uncomment this to add a dependency
|
|
||||||
# s.add_dependency "foo"
|
|
||||||
|
|
||||||
s.require_path = 'lib'
|
s.require_path = 'lib'
|
||||||
s.autorequire = GEM
|
s.autorequire = GEM
|
||||||
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec}/**/*")
|
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec}/**/*")
|
||||||
|
24
client-libraries/ruby/benchmarking/suite.rb
Normal file
24
client-libraries/ruby/benchmarking/suite.rb
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
def run_in_background(command)
|
||||||
|
fork { system command }
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_all_segments(&block)
|
||||||
|
0.upto(9) do |segment_number|
|
||||||
|
block_size = 100000
|
||||||
|
start_index = segment_number * block_size
|
||||||
|
end_index = start_index + block_size - 1
|
||||||
|
block.call(start_index, end_index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#with_all_segments do |start_index, end_index|
|
||||||
|
# puts "Initializing keys from #{start_index} to #{end_index}"
|
||||||
|
# system "ruby worker.rb initialize #{start_index} #{end_index} 0"
|
||||||
|
#end
|
||||||
|
|
||||||
|
with_all_segments do |start_index, end_index|
|
||||||
|
run_in_background "ruby worker.rb write #{start_index} #{end_index} 10"
|
||||||
|
run_in_background "ruby worker.rb read #{start_index} #{end_index} 1"
|
||||||
|
end
|
71
client-libraries/ruby/benchmarking/worker.rb
Normal file
71
client-libraries/ruby/benchmarking/worker.rb
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
BENCHMARK_ROOT = File.dirname(__FILE__)
|
||||||
|
REDIS_ROOT = File.join(BENCHMARK_ROOT, "..", "lib")
|
||||||
|
|
||||||
|
$: << REDIS_ROOT
|
||||||
|
require 'redis'
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
|
def show_usage
|
||||||
|
puts <<-EOL
|
||||||
|
Usage: worker.rb [read:write] <start_index> <end_index> <sleep_msec>
|
||||||
|
EOL
|
||||||
|
end
|
||||||
|
|
||||||
|
def shift_from_argv
|
||||||
|
value = ARGV.shift
|
||||||
|
unless value
|
||||||
|
show_usage
|
||||||
|
exit -1
|
||||||
|
end
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
operation = shift_from_argv.to_sym
|
||||||
|
start_index = shift_from_argv.to_i
|
||||||
|
end_index = shift_from_argv.to_i
|
||||||
|
sleep_msec = shift_from_argv.to_i
|
||||||
|
sleep_duration = sleep_msec/1000.0
|
||||||
|
|
||||||
|
redis = Redis.new
|
||||||
|
|
||||||
|
case operation
|
||||||
|
when :initialize
|
||||||
|
|
||||||
|
start_index.upto(end_index) do |i|
|
||||||
|
redis[i] = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
when :clear
|
||||||
|
|
||||||
|
start_index.upto(end_index) do |i|
|
||||||
|
redis.delete(i)
|
||||||
|
end
|
||||||
|
|
||||||
|
when :read, :write
|
||||||
|
|
||||||
|
puts "Starting to #{operation} at segment #{end_index + 1}"
|
||||||
|
|
||||||
|
loop do
|
||||||
|
t1 = Time.now
|
||||||
|
start_index.upto(end_index) do |i|
|
||||||
|
case operation
|
||||||
|
when :read
|
||||||
|
redis.get(i)
|
||||||
|
when :write
|
||||||
|
redis.incr(i)
|
||||||
|
else
|
||||||
|
raise "Unknown operation: #{operation}"
|
||||||
|
end
|
||||||
|
sleep sleep_duration
|
||||||
|
end
|
||||||
|
t2 = Time.now
|
||||||
|
|
||||||
|
requests_processed = end_index - start_index
|
||||||
|
time = t2 - t1
|
||||||
|
puts "#{t2.strftime("%H:%M")} [segment #{end_index + 1}] : Processed #{requests_processed} requests in #{time} seconds - #{(requests_processed/time).round} requests/sec"
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
raise "Unknown operation: #{operation}"
|
||||||
|
end
|
||||||
|
|
@ -9,10 +9,7 @@ class Redis
|
|||||||
@commands = []
|
@commands = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_response
|
def execute_command(data)
|
||||||
end
|
|
||||||
|
|
||||||
def write(data)
|
|
||||||
@commands << data
|
@commands << data
|
||||||
write_and_read if @commands.size >= BUFFER_SIZE
|
write_and_read if @commands.size >= BUFFER_SIZE
|
||||||
end
|
end
|
||||||
@ -22,7 +19,7 @@ class Redis
|
|||||||
end
|
end
|
||||||
|
|
||||||
def write_and_read
|
def write_and_read
|
||||||
@redis.write @commands.join
|
@redis.execute_command(@commands.join, true)
|
||||||
@redis.read_socket
|
@redis.read_socket
|
||||||
@commands.clear
|
@commands.clear
|
||||||
end
|
end
|
||||||
|
@ -3,14 +3,15 @@ require 'set'
|
|||||||
require File.join(File.dirname(__FILE__),'server')
|
require File.join(File.dirname(__FILE__),'server')
|
||||||
require File.join(File.dirname(__FILE__),'pipeline')
|
require File.join(File.dirname(__FILE__),'pipeline')
|
||||||
|
|
||||||
|
|
||||||
class RedisError < StandardError
|
class RedisError < StandardError
|
||||||
end
|
end
|
||||||
class RedisRenameError < StandardError
|
class RedisRenameError < StandardError
|
||||||
end
|
end
|
||||||
|
|
||||||
class Redis
|
class Redis
|
||||||
ERR = "-".freeze
|
ERR = "-".freeze
|
||||||
OK = 'OK'.freeze
|
OK = 'OK'.freeze
|
||||||
|
PONG = 'PONG'.freeze
|
||||||
SINGLE = '+'.freeze
|
SINGLE = '+'.freeze
|
||||||
BULK = '$'.freeze
|
BULK = '$'.freeze
|
||||||
MULTI = '*'.freeze
|
MULTI = '*'.freeze
|
||||||
@ -18,7 +19,6 @@ class Redis
|
|||||||
|
|
||||||
attr_reader :server
|
attr_reader :server
|
||||||
|
|
||||||
|
|
||||||
def initialize(opts={})
|
def initialize(opts={})
|
||||||
@opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
|
@opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
|
||||||
$debug = @opts[:debug]
|
$debug = @opts[:debug]
|
||||||
@ -33,7 +33,7 @@ class Redis
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
"#{host}:#{port}"
|
"#{host}:#{port} -> #{@db}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def port
|
def port
|
||||||
@ -44,76 +44,42 @@ class Redis
|
|||||||
@opts[:host]
|
@opts[:host]
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_socket_management(server, &block)
|
|
||||||
begin
|
|
||||||
socket = server.socket
|
|
||||||
block.call(socket)
|
|
||||||
#Timeout or server down
|
|
||||||
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Timeout::Error => e
|
|
||||||
server.close
|
|
||||||
puts "Client (#{server.inspect}) disconnected from server: #{e.inspect}\n" if $debug
|
|
||||||
retry
|
|
||||||
#Server down
|
|
||||||
rescue NoMethodError => e
|
|
||||||
puts "Client (#{server.inspect}) tryin server that is down: #{e.inspect}\n Dying!" if $debug
|
|
||||||
raise Errno::ECONNREFUSED
|
|
||||||
#exit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def monitor
|
|
||||||
with_socket_management(@server) do |socket|
|
|
||||||
trap("INT") { puts "\nGot ^C! Dying!"; exit }
|
|
||||||
write "MONITOR\r\n"
|
|
||||||
puts "Now Monitoring..."
|
|
||||||
socket.read(12)
|
|
||||||
loop do
|
|
||||||
x = socket.gets
|
|
||||||
puts x unless x.nil?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def quit
|
def quit
|
||||||
write "QUIT\r\n"
|
execute_command("QUIT\r\n", true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ping
|
||||||
|
execute_command("PING\r\n") == PONG
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_db(index)
|
def select_db(index)
|
||||||
@db = index
|
@db = index
|
||||||
write "SELECT #{index}\r\n"
|
execute_command("SELECT #{index}\r\n")
|
||||||
get_response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def flush_db
|
def flush_db
|
||||||
write "FLUSHDB\r\n"
|
execute_command("FLUSHDB\r\n") == OK
|
||||||
get_response == OK
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def flush_all
|
def flush_all
|
||||||
ensure_retry do
|
|
||||||
puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
|
puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
|
||||||
trap('INT') {quit; return false}
|
trap('INT') {quit; return false}
|
||||||
sleep 5
|
sleep 5
|
||||||
write "FLUSHALL\r\n"
|
execute_command("FLUSHALL\r\n") == OK
|
||||||
get_response == OK
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_save
|
def last_save
|
||||||
write "LASTSAVE\r\n"
|
execute_command("LASTSAVE\r\n").to_i
|
||||||
get_response.to_i
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def bgsave
|
def bgsave
|
||||||
write "BGSAVE\r\n"
|
execute_command("BGSAVE\r\n") == OK
|
||||||
get_response == OK
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def info
|
def info
|
||||||
info = {}
|
info = {}
|
||||||
write("INFO\r\n")
|
x = execute_command("INFO\r\n")
|
||||||
x = get_response
|
x.each_line do |kv|
|
||||||
x.each do |kv|
|
|
||||||
k,v = kv.split(':', 2)
|
k,v = kv.split(':', 2)
|
||||||
k,v = k.chomp, v = v.chomp
|
k,v = k.chomp, v = v.chomp
|
||||||
info[k.to_sym] = v
|
info[k.to_sym] = v
|
||||||
@ -121,6 +87,231 @@ class Redis
|
|||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def keys(glob)
|
||||||
|
execute_command("KEYS #{glob}\r\n").split(' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
def rename!(oldkey, newkey)
|
||||||
|
execute_command("RENAME #{oldkey} #{newkey}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def rename(oldkey, newkey)
|
||||||
|
case execute_command("RENAMENX #{oldkey} #{newkey}\r\n")
|
||||||
|
when -1
|
||||||
|
raise RedisRenameError, "source key: #{oldkey} does not exist"
|
||||||
|
when 0
|
||||||
|
raise RedisRenameError, "target key: #{oldkey} already exists"
|
||||||
|
when -3
|
||||||
|
raise RedisRenameError, "source and destination keys are the same"
|
||||||
|
when 1
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def key?(key)
|
||||||
|
execute_command("EXISTS #{key}\r\n") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
execute_command("DEL #{key}\r\n") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
get(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(key)
|
||||||
|
execute_command("GET #{key}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def mget(*keys)
|
||||||
|
execute_command("MGET #{keys.join(' ')}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def incr(key, increment=nil)
|
||||||
|
if increment
|
||||||
|
execute_command("INCRBY #{key} #{increment}\r\n")
|
||||||
|
else
|
||||||
|
execute_command("INCR #{key}\r\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def decr(key, decrement=nil)
|
||||||
|
if decrement
|
||||||
|
execute_command("DECRBY #{key} #{decrement}\r\n")
|
||||||
|
else
|
||||||
|
execute_command("DECR #{key}\r\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def randkey
|
||||||
|
execute_command("RANDOMKEY\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_length(key)
|
||||||
|
case i = execute_command("LLEN #{key}\r\n")
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} does not hold a list value"
|
||||||
|
else
|
||||||
|
i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type?(key)
|
||||||
|
execute_command("TYPE #{key}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_tail(key, val)
|
||||||
|
execute_command("RPUSH #{key} #{value_to_wire(val)}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_head(key, val)
|
||||||
|
execute_command("LPUSH #{key} #{value_to_wire(val)}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop_head(key)
|
||||||
|
execute_command("LPOP #{key}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop_tail(key)
|
||||||
|
execute_command("RPOP #{key}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_set(key, index, val)
|
||||||
|
execute_command("LSET #{key} #{index} #{value_to_wire(val)}\r\n") == OK
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_range(key, start, ending)
|
||||||
|
execute_command("LRANGE #{key} #{start} #{ending}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_trim(key, start, ending)
|
||||||
|
execute_command("LTRIM #{key} #{start} #{ending}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_index(key, index)
|
||||||
|
execute_command("LINDEX #{key} #{index}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_rm(key, count, val)
|
||||||
|
case num = execute_command("LREM #{key} #{count} #{value_to_wire(val)}\r\n")
|
||||||
|
when -1
|
||||||
|
raise RedisError, "key: #{key} does not exist"
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} does not hold a list value"
|
||||||
|
else
|
||||||
|
num
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_add(key, member)
|
||||||
|
case execute_command("SADD #{key} #{value_to_wire(member)}\r\n")
|
||||||
|
when 1
|
||||||
|
true
|
||||||
|
when 0
|
||||||
|
false
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} contains a non set value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_delete(key, member)
|
||||||
|
case execute_command("SREM #{key} #{value_to_wire(member)}\r\n")
|
||||||
|
when 1
|
||||||
|
true
|
||||||
|
when 0
|
||||||
|
false
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} contains a non set value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_count(key)
|
||||||
|
case i = execute_command("SCARD #{key}\r\n")
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} contains a non set value"
|
||||||
|
else
|
||||||
|
i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_member?(key, member)
|
||||||
|
case execute_command("SISMEMBER #{key} #{value_to_wire(member)}\r\n")
|
||||||
|
when 1
|
||||||
|
true
|
||||||
|
when 0
|
||||||
|
false
|
||||||
|
when -2
|
||||||
|
raise RedisError, "key: #{key} contains a non set value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_members(key)
|
||||||
|
Set.new(execute_command("SMEMBERS #{key}\r\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_intersect(*keys)
|
||||||
|
Set.new(execute_command("SINTER #{keys.join(' ')}\r\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_inter_store(destkey, *keys)
|
||||||
|
execute_command("SINTERSTORE #{destkey} #{keys.join(' ')}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_union(*keys)
|
||||||
|
Set.new(execute_command("SUNION #{keys.join(' ')}\r\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_union_store(destkey, *keys)
|
||||||
|
execute_command("SUNIONSTORE #{destkey} #{keys.join(' ')}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_diff(*keys)
|
||||||
|
Set.new(execute_command("SDIFF #{keys.join(' ')}\r\n"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_diff_store(destkey, *keys)
|
||||||
|
execute_command("SDIFFSTORE #{destkey} #{keys.join(' ')}\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_move(srckey, destkey, member)
|
||||||
|
execute_command("SMOVE #{srckey} #{destkey} #{value_to_wire(member)}\r\n") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort(key, opts={})
|
||||||
|
cmd = "SORT #{key}"
|
||||||
|
cmd << " BY #{opts[:by]}" if opts[:by]
|
||||||
|
cmd << " GET #{[opts[:get]].flatten * ' GET '}" if opts[:get]
|
||||||
|
cmd << " INCR #{opts[:incr]}" if opts[:incr]
|
||||||
|
cmd << " DEL #{opts[:del]}" if opts[:del]
|
||||||
|
cmd << " DECR #{opts[:decr]}" if opts[:decr]
|
||||||
|
cmd << " #{opts[:order]}" if opts[:order]
|
||||||
|
cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
|
||||||
|
cmd << "\r\n"
|
||||||
|
execute_command(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, val)
|
||||||
|
set(key,val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set(key, val, expiry=nil)
|
||||||
|
s = execute_command("SET #{key} #{value_to_wire(val)}\r\n") == OK
|
||||||
|
return expire(key, expiry) if s && expiry
|
||||||
|
s
|
||||||
|
end
|
||||||
|
|
||||||
|
def dbsize
|
||||||
|
execute_command("DBSIZE\r\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def expire(key, expiry=nil)
|
||||||
|
execute_command("EXPIRE #{key} #{expiry}\r\n") == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_unless_exists(key, val)
|
||||||
|
execute_command("SETNX #{key} #{value_to_wire(val)}\r\n") == 1
|
||||||
|
end
|
||||||
|
|
||||||
def bulk_reply
|
def bulk_reply
|
||||||
begin
|
begin
|
||||||
@ -134,266 +325,16 @@ class Redis
|
|||||||
end
|
end
|
||||||
|
|
||||||
def write(data)
|
def write(data)
|
||||||
with_socket_management(@server) do |socket|
|
|
||||||
puts "writing: #{data}" if $debug
|
puts "writing: #{data}" if $debug
|
||||||
socket.write(data)
|
@socket.write(data)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch(len)
|
def read(length = 0)
|
||||||
with_socket_management(@server) do |socket|
|
length = read_proto unless length > 0
|
||||||
len = [0, len.to_i].max
|
res = @socket.read(length)
|
||||||
res = socket.read(len + 2)
|
|
||||||
res = res.chomp if res
|
|
||||||
res
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(length = read_proto)
|
|
||||||
with_socket_management(@server) do |socket|
|
|
||||||
res = socket.read(length)
|
|
||||||
puts "read is #{res.inspect}" if $debug
|
puts "read is #{res.inspect}" if $debug
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def keys(glob)
|
|
||||||
write "KEYS #{glob}\r\n"
|
|
||||||
get_response.split(' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename!(oldkey, newkey)
|
|
||||||
write "RENAME #{oldkey} #{newkey}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def rename(oldkey, newkey)
|
|
||||||
write "RENAMENX #{oldkey} #{newkey}\r\n"
|
|
||||||
case get_response
|
|
||||||
when -1
|
|
||||||
raise RedisRenameError, "source key: #{oldkey} does not exist"
|
|
||||||
when 0
|
|
||||||
raise RedisRenameError, "target key: #{oldkey} already exists"
|
|
||||||
when -3
|
|
||||||
raise RedisRenameError, "source and destination keys are the same"
|
|
||||||
when 1
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def key?(key)
|
|
||||||
write "EXISTS #{key}\r\n"
|
|
||||||
get_response == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(key)
|
|
||||||
write "DEL #{key}\r\n"
|
|
||||||
get_response == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](key)
|
|
||||||
get(key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get(key)
|
|
||||||
write "GET #{key}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def mget(*keys)
|
|
||||||
write "MGET #{keys.join(' ')}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def incr(key, increment=nil)
|
|
||||||
if increment
|
|
||||||
write "INCRBY #{key} #{increment}\r\n"
|
|
||||||
else
|
|
||||||
write "INCR #{key}\r\n"
|
|
||||||
end
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def decr(key, decrement=nil)
|
|
||||||
if decrement
|
|
||||||
write "DECRBY #{key} #{decrement}\r\n"
|
|
||||||
else
|
|
||||||
write "DECR #{key}\r\n"
|
|
||||||
end
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def randkey
|
|
||||||
write "RANDOMKEY\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_length(key)
|
|
||||||
write "LLEN #{key}\r\n"
|
|
||||||
case i = get_response
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} does not hold a list value"
|
|
||||||
else
|
|
||||||
i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def type?(key)
|
|
||||||
write "TYPE #{key}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_tail(key, string)
|
|
||||||
write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_head(key, string)
|
|
||||||
write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_head(key)
|
|
||||||
write "LPOP #{key}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_tail(key)
|
|
||||||
write "RPOP #{key}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_set(key, index, val)
|
|
||||||
write "LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n"
|
|
||||||
get_response == OK
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_range(key, start, ending)
|
|
||||||
write "LRANGE #{key} #{start} #{ending}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_trim(key, start, ending)
|
|
||||||
write "LTRIM #{key} #{start} #{ending}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_index(key, index)
|
|
||||||
write "LINDEX #{key} #{index}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_rm(key, count, value)
|
|
||||||
write "LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n"
|
|
||||||
case num = get_response
|
|
||||||
when -1
|
|
||||||
raise RedisError, "key: #{key} does not exist"
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} does not hold a list value"
|
|
||||||
else
|
|
||||||
num
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_add(key, member)
|
|
||||||
write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
|
||||||
case get_response
|
|
||||||
when 1
|
|
||||||
true
|
|
||||||
when 0
|
|
||||||
false
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} contains a non set value"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_delete(key, member)
|
|
||||||
write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
|
||||||
case get_response
|
|
||||||
when 1
|
|
||||||
true
|
|
||||||
when 0
|
|
||||||
false
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} contains a non set value"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_count(key)
|
|
||||||
write "SCARD #{key}\r\n"
|
|
||||||
case i = get_response
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} contains a non set value"
|
|
||||||
else
|
|
||||||
i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_member?(key, member)
|
|
||||||
write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
|
||||||
case get_response
|
|
||||||
when 1
|
|
||||||
true
|
|
||||||
when 0
|
|
||||||
false
|
|
||||||
when -2
|
|
||||||
raise RedisError, "key: #{key} contains a non set value"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_members(key)
|
|
||||||
write "SMEMBERS #{key}\r\n"
|
|
||||||
Set.new(get_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_intersect(*keys)
|
|
||||||
write "SINTER #{keys.join(' ')}\r\n"
|
|
||||||
Set.new(get_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_inter_store(destkey, *keys)
|
|
||||||
write "SINTERSTORE #{destkey} #{keys.join(' ')}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_union(*keys)
|
|
||||||
write "SUNION #{keys.join(' ')}\r\n"
|
|
||||||
Set.new(get_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_union_store(destkey, *keys)
|
|
||||||
write "SUNIONSTORE #{destkey} #{keys.join(' ')}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_diff(*keys)
|
|
||||||
write "SDIFF #{keys.join(' ')}\r\n"
|
|
||||||
Set.new(get_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_diff_store(destkey, *keys)
|
|
||||||
write "SDIFFSTORE #{destkey} #{keys.join(' ')}\r\n"
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_move(srckey, destkey, member)
|
|
||||||
write "SMOVE #{srckey} #{destkey} #{member.to_s.size}\r\n#{member}\r\n"
|
|
||||||
get_response == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def sort(key, opts={})
|
|
||||||
cmd = "SORT #{key}"
|
|
||||||
cmd << " BY #{opts[:by]}" if opts[:by]
|
|
||||||
cmd << " GET #{opts[:get]}" if opts[:get]
|
|
||||||
cmd << " INCR #{opts[:incr]}" if opts[:incr]
|
|
||||||
cmd << " DEL #{opts[:del]}" if opts[:del]
|
|
||||||
cmd << " DECR #{opts[:decr]}" if opts[:decr]
|
|
||||||
cmd << " #{opts[:order]}" if opts[:order]
|
|
||||||
cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
|
|
||||||
cmd << "\r\n"
|
|
||||||
write(cmd)
|
|
||||||
get_response
|
|
||||||
end
|
|
||||||
|
|
||||||
def multi_bulk
|
def multi_bulk
|
||||||
res = read_proto
|
res = read_proto
|
||||||
@ -418,28 +359,6 @@ class Redis
|
|||||||
r
|
r
|
||||||
end
|
end
|
||||||
|
|
||||||
def []=(key, val)
|
|
||||||
set(key,val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def set(key, val, expiry=nil)
|
|
||||||
write("SET #{key} #{val.to_s.size}\r\n#{val}\r\n")
|
|
||||||
s = get_response == OK
|
|
||||||
return expire(key, expiry) if s && expiry
|
|
||||||
s
|
|
||||||
end
|
|
||||||
|
|
||||||
def expire(key, expiry=nil)
|
|
||||||
write("EXPIRE #{key} #{expiry}\r\n")
|
|
||||||
get_response == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_unless_exists(key, val)
|
|
||||||
write "SETNX #{key} #{val.to_s.size}\r\n#{val}\r\n"
|
|
||||||
get_response == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_code_reply
|
def status_code_reply
|
||||||
begin
|
begin
|
||||||
res = read_proto
|
res = read_proto
|
||||||
@ -453,12 +372,25 @@ class Redis
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_response
|
def execute_command(command, ignore_response=false)
|
||||||
begin
|
ss = server.socket
|
||||||
rtype = get_reply
|
unless ss.object_id == @socket.object_id
|
||||||
rescue => e
|
@socket = ss
|
||||||
raise RedisError, e.inspect
|
puts "Socket changed, selecting DB" if $debug
|
||||||
|
unless command[0..6] == 'SELECT'
|
||||||
|
#BTM - Ugh- DRY but better than infinite recursion
|
||||||
|
write("SELECT #{@db}\r\n")
|
||||||
|
get_response
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
write(command)
|
||||||
|
get_response unless ignore_response
|
||||||
|
rescue Errno::ECONNRESET, Errno::EPIPE, NoMethodError, Timeout::Error => e
|
||||||
|
raise RedisError, "Connection error"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_response
|
||||||
|
rtype = get_reply
|
||||||
puts "reply_type is #{rtype.inspect}" if $debug
|
puts "reply_type is #{rtype.inspect}" if $debug
|
||||||
case rtype
|
case rtype
|
||||||
when SINGLE
|
when SINGLE
|
||||||
@ -512,13 +444,21 @@ class Redis
|
|||||||
end
|
end
|
||||||
|
|
||||||
def read_proto
|
def read_proto
|
||||||
with_socket_management(@server) do |socket|
|
res = @socket.readline
|
||||||
if res = socket.gets
|
|
||||||
x = res.chomp
|
x = res.chomp
|
||||||
puts "read_proto is #{x.inspect}\n\n" if $debug
|
puts "read_proto is #{x.inspect}\n\n" if $debug
|
||||||
x.to_i
|
x.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def value_to_wire(value)
|
||||||
|
value_str = value.to_s
|
||||||
|
if value_str.respond_to?(:bytesize)
|
||||||
|
value_size = value_str.bytesize
|
||||||
|
else
|
||||||
|
value_size = value_str.size
|
||||||
end
|
end
|
||||||
|
"#{value_size}\r\n#{value_str}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -24,12 +24,6 @@ end
|
|||||||
|
|
||||||
class Server
|
class Server
|
||||||
|
|
||||||
##
|
|
||||||
# The amount of time to wait before attempting to re-establish a
|
|
||||||
# connection with a server that is marked dead.
|
|
||||||
|
|
||||||
RETRY_DELAY = 30.0
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# The host the redis server is running on.
|
# The host the redis server is running on.
|
||||||
|
|
||||||
@ -40,16 +34,6 @@ class Server
|
|||||||
|
|
||||||
attr_reader :port
|
attr_reader :port
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
|
|
||||||
attr_reader :replica
|
|
||||||
|
|
||||||
##
|
|
||||||
# The time of next retry if the connection is dead.
|
|
||||||
|
|
||||||
attr_reader :retry
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# A text status string describing the state of the server.
|
# A text status string describing the state of the server.
|
||||||
|
|
||||||
@ -67,7 +51,6 @@ class Server
|
|||||||
@port = port.to_i
|
@port = port.to_i
|
||||||
|
|
||||||
@sock = nil
|
@sock = nil
|
||||||
@retry = nil
|
|
||||||
@status = 'NOT CONNECTED'
|
@status = 'NOT CONNECTED'
|
||||||
@timeout = timeout
|
@timeout = timeout
|
||||||
end
|
end
|
||||||
@ -83,38 +66,31 @@ class Server
|
|||||||
# Returns the connected socket object on success or nil on failure.
|
# Returns the connected socket object on success or nil on failure.
|
||||||
|
|
||||||
def socket
|
def socket
|
||||||
return @sock if @sock and not @sock.closed?
|
return @sock if socket_alive?
|
||||||
|
close
|
||||||
@sock = nil
|
|
||||||
|
|
||||||
# If the host was dead, don't retry for a while.
|
|
||||||
return if @retry and @retry > Time.now
|
|
||||||
|
|
||||||
# Attempt to connect if not already connected.
|
# Attempt to connect if not already connected.
|
||||||
begin
|
begin
|
||||||
@sock = connect_to(@host, @port, @timeout)
|
@sock = connect_to(@host, @port, @timeout)
|
||||||
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
||||||
@retry = nil
|
|
||||||
@status = 'CONNECTED'
|
@status = 'CONNECTED'
|
||||||
rescue Errno::EPIPE, Errno::ECONNREFUSED => e
|
rescue Errno::EPIPE, Errno::ECONNREFUSED => e
|
||||||
puts "Socket died... socket: #{@sock.inspect}\n" if $debug
|
puts "Socket died... : #{e}\n" if $debug
|
||||||
@sock.close
|
|
||||||
retry
|
retry
|
||||||
rescue SocketError, SystemCallError, IOError => err
|
rescue SocketError, SystemCallError, IOError => err
|
||||||
puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
|
puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
|
||||||
mark_dead err
|
|
||||||
end
|
end
|
||||||
@sock
|
@sock
|
||||||
end
|
end
|
||||||
|
|
||||||
def connect_to(host, port, timeout=nil)
|
def connect_to(host, port, timeout=nil)
|
||||||
socket = TCPSocket.new(host, port, 0)
|
socket = TCPSocket.new(host, port)
|
||||||
|
socket.set_encoding(Encoding::BINARY) if socket.respond_to?(:set_encoding)
|
||||||
if timeout
|
if timeout
|
||||||
socket.instance_eval <<-EOR
|
socket.instance_eval <<-EOR
|
||||||
alias :blocking_gets :gets
|
alias :blocking_readline :readline
|
||||||
def gets(*args)
|
def readline(*args)
|
||||||
RedisTimer.timeout(#{timeout}) do
|
RedisTimer.timeout(#{timeout}) do
|
||||||
self.blocking_gets(*args)
|
self.blocking_readline(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias :blocking_read :read
|
alias :blocking_read :read
|
||||||
@ -134,27 +110,22 @@ class Server
|
|||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Close the connection to the redis server targeted by this
|
# Close the connection to the redis server targeted by this
|
||||||
# object. The server is not considered dead.
|
# object.
|
||||||
|
|
||||||
def close
|
def close
|
||||||
@sock.close if @sock && !@sock.closed?
|
@sock.close if !@sock.nil? && !@sock.closed?
|
||||||
@sock = nil
|
@sock = nil
|
||||||
@retry = nil
|
|
||||||
@status = "NOT CONNECTED"
|
@status = "NOT CONNECTED"
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
private
|
||||||
# Mark the server as dead and close its socket.
|
def socket_alive?
|
||||||
def mark_dead(error)
|
#BTM - TODO - FileStat is borked under JRuby
|
||||||
@sock.close if @sock && !@sock.closed?
|
unless defined?(JRUBY_VERSION)
|
||||||
@sock = nil
|
!@sock.nil? && !@sock.closed? && @sock.stat.readable?
|
||||||
@retry = Time.now #+ RETRY_DELAY
|
else
|
||||||
|
!@sock.nil? && !@sock.closed?
|
||||||
reason = "#{error.class.name}: #{error.message}"
|
end
|
||||||
@status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
|
|
||||||
puts @status
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = %q{redis}
|
s.name = %q{redis}
|
||||||
s.version = "0.0.3.4"
|
s.version = "0.0.3.5"
|
||||||
|
|
||||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
s.authors = ["Ezra Zygmuntowicz", "Taylor Weibley", "Matthew Clark"]
|
s.authors = ["Ezra Zygmuntowicz", "Taylor Weibley", "Matthew Clark"]
|
||||||
|
@ -13,8 +13,8 @@ end
|
|||||||
|
|
||||||
describe "redis" do
|
describe "redis" do
|
||||||
before(:all) do
|
before(:all) do
|
||||||
@r = Redis.new
|
# use database 15 for testing so we dont accidentally step on you real data
|
||||||
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
|
@r = Redis.new :db => 15
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@ -29,6 +29,9 @@ describe "redis" do
|
|||||||
@r.quit
|
@r.quit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should be able to PING' do
|
||||||
|
@r.ping.should == true
|
||||||
|
end
|
||||||
|
|
||||||
it "should be able to GET a key" do
|
it "should be able to GET a key" do
|
||||||
@r['foo'].should == 'bar'
|
@r['foo'].should == 'bar'
|
||||||
@ -103,6 +106,15 @@ describe "redis" do
|
|||||||
@r['bar'].should == 'ohai'
|
@r['bar'].should == 'ohai'
|
||||||
end
|
end
|
||||||
#
|
#
|
||||||
|
it "should be able to get DBSIZE of the database" do
|
||||||
|
@r.delete 'foo'
|
||||||
|
dbsize_without_foo = @r.dbsize
|
||||||
|
@r['foo'] = 0
|
||||||
|
dbsize_with_foo = @r.dbsize
|
||||||
|
|
||||||
|
dbsize_with_foo.should == dbsize_without_foo + 1
|
||||||
|
end
|
||||||
|
#
|
||||||
it "should be able to EXPIRE a key" do
|
it "should be able to EXPIRE a key" do
|
||||||
@r['foo'] = 'bar'
|
@r['foo'] = 'bar'
|
||||||
@r.expire('foo', 1)
|
@r.expire('foo', 1)
|
||||||
@ -287,6 +299,7 @@ describe "redis" do
|
|||||||
@r.set_inter_store('newone', 'set', 'set2').should == 'OK'
|
@r.set_inter_store('newone', 'set', 'set2').should == 'OK'
|
||||||
@r.set_members('newone').should == Set.new(['key2'])
|
@r.set_members('newone').should == Set.new(['key2'])
|
||||||
@r.delete('set')
|
@r.delete('set')
|
||||||
|
@r.delete('set2')
|
||||||
end
|
end
|
||||||
#
|
#
|
||||||
it "should be able to do set union" do
|
it "should be able to do set union" do
|
||||||
@ -296,6 +309,7 @@ describe "redis" do
|
|||||||
@r.set_add "set2", 'key3'
|
@r.set_add "set2", 'key3'
|
||||||
@r.set_union('set', 'set2').should == Set.new(['key1','key2','key3'])
|
@r.set_union('set', 'set2').should == Set.new(['key1','key2','key3'])
|
||||||
@r.delete('set')
|
@r.delete('set')
|
||||||
|
@r.delete('set2')
|
||||||
end
|
end
|
||||||
#
|
#
|
||||||
it "should be able to do set union and store the results in a key" do
|
it "should be able to do set union and store the results in a key" do
|
||||||
@ -306,28 +320,29 @@ describe "redis" do
|
|||||||
@r.set_union_store('newone', 'set', 'set2').should == 'OK'
|
@r.set_union_store('newone', 'set', 'set2').should == 'OK'
|
||||||
@r.set_members('newone').should == Set.new(['key1','key2','key3'])
|
@r.set_members('newone').should == Set.new(['key1','key2','key3'])
|
||||||
@r.delete('set')
|
@r.delete('set')
|
||||||
|
@r.delete('set2')
|
||||||
|
end
|
||||||
|
#
|
||||||
|
it "should be able to do set difference" do
|
||||||
|
@r.set_add "set", 'a'
|
||||||
|
@r.set_add "set", 'b'
|
||||||
|
@r.set_add "set2", 'b'
|
||||||
|
@r.set_add "set2", 'c'
|
||||||
|
@r.set_diff('set', 'set2').should == Set.new(['a'])
|
||||||
|
@r.delete('set')
|
||||||
|
@r.delete('set2')
|
||||||
|
end
|
||||||
|
#
|
||||||
|
it "should be able to do set difference and store the results in a key" do
|
||||||
|
@r.set_add "set", 'a'
|
||||||
|
@r.set_add "set", 'b'
|
||||||
|
@r.set_add "set2", 'b'
|
||||||
|
@r.set_add "set2", 'c'
|
||||||
|
@r.set_diff_store('newone', 'set', 'set2')
|
||||||
|
@r.set_members('newone').should == Set.new(['a'])
|
||||||
|
@r.delete('set')
|
||||||
|
@r.delete('set2')
|
||||||
end
|
end
|
||||||
|
|
||||||
# these don't seem to be implemented in redis head?
|
|
||||||
# it "should be able to do set difference" do
|
|
||||||
# @r.set_add "set", 'key1'
|
|
||||||
# @r.set_add "set", 'key2'
|
|
||||||
# @r.set_add "set2", 'key2'
|
|
||||||
# @r.set_add "set2", 'key3'
|
|
||||||
# @r.set_diff('set', 'set2').should == Set.new(['key1','key3'])
|
|
||||||
# @r.delete('set')
|
|
||||||
# end
|
|
||||||
# #
|
|
||||||
# it "should be able to do set difference and store the results in a key" do
|
|
||||||
# @r.set_add "set", 'key1'
|
|
||||||
# @r.set_add "set", 'key2'
|
|
||||||
# @r.set_add "set2", 'key2'
|
|
||||||
# @r.set_add "set2", 'key3'
|
|
||||||
# count = @r.set_diff_store('newone', 'set', 'set2')
|
|
||||||
# count.should == 3
|
|
||||||
# @r.set_members('newone').should == Set.new(['key1','key3'])
|
|
||||||
# @r.delete('set')
|
|
||||||
# end
|
|
||||||
#
|
#
|
||||||
it "should be able move elements from one set to another" do
|
it "should be able move elements from one set to another" do
|
||||||
@r.set_add 'set1', 'a'
|
@r.set_add 'set1', 'a'
|
||||||
@ -350,6 +365,23 @@ describe "redis" do
|
|||||||
@r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie']
|
@r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie']
|
||||||
@r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj']
|
@r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should be able to handle array of :get using SORT" do
|
||||||
|
@r['dog:1:name'] = 'louie'
|
||||||
|
@r['dog:1:breed'] = 'mutt'
|
||||||
|
@r.push_tail 'dogs', 1
|
||||||
|
@r['dog:2:name'] = 'lucy'
|
||||||
|
@r['dog:2:breed'] = 'poodle'
|
||||||
|
@r.push_tail 'dogs', 2
|
||||||
|
@r['dog:3:name'] = 'max'
|
||||||
|
@r['dog:3:breed'] = 'hound'
|
||||||
|
@r.push_tail 'dogs', 3
|
||||||
|
@r['dog:4:name'] = 'taj'
|
||||||
|
@r['dog:4:breed'] = 'terrier'
|
||||||
|
@r.push_tail 'dogs', 4
|
||||||
|
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1]).should == ['louie', 'mutt']
|
||||||
|
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1], :order => 'desc alpha').should == ['taj', 'terrier']
|
||||||
|
end
|
||||||
#
|
#
|
||||||
it "should provide info" do
|
it "should provide info" do
|
||||||
[:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
|
[:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
|
||||||
@ -407,4 +439,7 @@ describe "redis" do
|
|||||||
@r.pop_head('list').should == '42'
|
@r.pop_head('list').should == '42'
|
||||||
@r.delete('list')
|
@r.delete('list')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should select db on connection"
|
||||||
|
it "should re-select db on reconnection"
|
||||||
end
|
end
|
||||||
|
22
client-libraries/ruby/spec/server_spec.rb
Normal file
22
client-libraries/ruby/spec/server_spec.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
require File.dirname(__FILE__) + '/spec_helper'
|
||||||
|
|
||||||
|
describe "Server" do
|
||||||
|
before(:each) do
|
||||||
|
@server = Server.new 'localhost', '6379'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should checkout active connections" do
|
||||||
|
threads = []
|
||||||
|
10.times do
|
||||||
|
threads << Thread.new do
|
||||||
|
lambda {
|
||||||
|
socket = @server.socket
|
||||||
|
socket.close
|
||||||
|
socket.write("INFO\r\n")
|
||||||
|
socket.read(1)
|
||||||
|
}.should_not raise_error(Exception)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -64,7 +64,7 @@ namespace :redis do
|
|||||||
RedisRunner.attach
|
RedisRunner.attach
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Install the lastest redis from svn'
|
desc 'Install the lastest verison of Redis from Github (requires git, duh)'
|
||||||
task :install => [:about, :download, :make] do
|
task :install => [:about, :download, :make] do
|
||||||
%w(redis-benchmark redis-cli redis-server).each do |bin|
|
%w(redis-benchmark redis-cli redis-server).each do |bin|
|
||||||
sh "sudo cp /tmp/redis/#{bin} /usr/bin/"
|
sh "sudo cp /tmp/redis/#{bin} /usr/bin/"
|
||||||
|
2
redis.c
2
redis.c
@ -1528,7 +1528,7 @@ static void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
char *err = "-ERR max number of clients reached\r\n";
|
char *err = "-ERR max number of clients reached\r\n";
|
||||||
|
|
||||||
/* That's a best effort error message, don't check write errors */
|
/* That's a best effort error message, don't check write errors */
|
||||||
(void)write(c->fd,err,strlen(err));
|
(void) write(c->fd,err,strlen(err));
|
||||||
freeClient(c);
|
freeClient(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user