提交 d7fc9edb 编写于 作者: A antirez

client libraries synched in git

上级 e083d752
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 INFO command now reports replication info
2009-05-25 minor fix to RubyRedis about bulk commands sent without arguments
......
+ finish command implementations
= finish unit tests
Only a few left, to test the SORT command's edge cases (e.g. BY pattern)
+ 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.)
- benchmarking
command handlers:
- support DEL as vararg
- support MLLEN and MSCARD
unit tests:
- sort with limit
- sort lexicographically
- sort with pattern and weights
extras:
- benchmarking "test" app
- 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)
......@@ -663,16 +663,24 @@ namespace redis
const client::string_type & by_pattern,
client::int_type limit_start,
client::int_type limit_end,
const client::string_type & get_pattern,
const client::string_vector & get_patterns,
client::sort_order order,
bool lexicographically)
{
send_(makecmd("SORT") << key
<< " BY " << by_pattern
<< " LIMIT " << limit_start << ' ' << limit_end
<< " GET " << get_pattern
<< (order == sort_order_ascending ? " ASC" : " DESC")
<< (lexicographically ? " ALPHA" : ""));
makecmd m("SORT");
m << key
<< " BY " << by_pattern
<< " LIMIT " << limit_start << ' ' << limit_end;
client::string_vector::const_iterator it = get_patterns.begin();
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);
}
......@@ -681,7 +689,7 @@ namespace redis
{
send_(makecmd("SAVE", true));
recv_ok_reply_();
e.g. }
}
void client::bgsave()
{
......
......@@ -421,7 +421,7 @@ namespace redis
const string_type & by_pattern,
int_type limit_start,
int_type limit_end,
const string_type & get_pattern,
const string_vector & get_patterns,
sort_order order = sort_order_ascending,
bool lexicographically = false);
......
......@@ -316,7 +316,7 @@ redis_commands = {
function(client, command)
-- let's fire and forget! the connection is closed as soon
-- as the SHUTDOWN command is received by the server.
network.write(command .. protocol.newline)
network.write(client, command .. protocol.newline)
end
),
......
nohup.out
redis/*
rdsrv
pkg/*
\ No newline at end of file
pkg/*
.idea
......@@ -12,7 +12,10 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
## Dependencies
1. redis -
1. rspec -
sudo gem install rspec
2. redis -
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
3. svn - git is the new black, but we need it for the google codes.
3. git - git is the new black.
## Setup
......
......@@ -8,10 +8,10 @@ require 'tasks/redis.tasks'
GEM = 'redis'
GEM_NAME = 'redis'
GEM_VERSION = '0.0.3.3'
GEM_VERSION = '0.0.3.4'
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark']
EMAIL = "matt.clark@punchstock.com"
HOMEPAGE = "http://github.com/winescout/redis-rb"
EMAIL = "ez@engineyard.com"
HOMEPAGE = "http://github.com/ezmobius/redis-rb"
SUMMARY = "Ruby client library for redis key value storage server"
spec = Gem::Specification.new do |s|
......@@ -25,10 +25,7 @@ spec = Gem::Specification.new do |s|
s.authors = AUTHORS
s.email = EMAIL
s.homepage = HOMEPAGE
# Uncomment this to add a dependency
# s.add_dependency "foo"
s.add_dependency "rspec"
s.require_path = 'lib'
s.autorequire = GEM
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec}/**/*")
......
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
\ No newline at end of file
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 = []
end
def get_response
end
def write(data)
def execute_command(data)
@commands << data
write_and_read if @commands.size >= BUFFER_SIZE
end
......@@ -22,10 +19,10 @@ class Redis
end
def write_and_read
@redis.write @commands.join
@redis.execute_command(@commands.join, true)
@redis.read_socket
@commands.clear
end
end
end
\ No newline at end of file
end
......@@ -3,14 +3,15 @@ require 'set'
require File.join(File.dirname(__FILE__),'server')
require File.join(File.dirname(__FILE__),'pipeline')
class RedisError < StandardError
end
class RedisRenameError < StandardError
end
class Redis
ERR = "-".freeze
OK = 'OK'.freeze
PONG = 'PONG'.freeze
SINGLE = '+'.freeze
BULK = '$'.freeze
MULTI = '*'.freeze
......@@ -18,22 +19,21 @@ class Redis
attr_reader :server
def initialize(opts={})
@opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
$debug = @opts[:debug]
@db = @opts[:db]
@server = Server.new(@opts[:host], @opts[:port], (@opts[:timeout]||10))
end
def pipelined
pipeline = Pipeline.new(self)
yield pipeline
pipeline.finish
end
def to_s
"#{host}:#{port}"
"#{host}:#{port} -> #{@db}"
end
def port
......@@ -44,76 +44,42 @@ class Redis
@opts[:host]
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
def quit
execute_command("QUIT\r\n", true)
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
def ping
execute_command("PING\r\n") == PONG
end
def quit
write "QUIT\r\n"
end
def select_db(index)
@db = index
write "SELECT #{index}\r\n"
get_response
execute_command("SELECT #{index}\r\n")
end
def flush_db
write "FLUSHDB\r\n"
get_response == OK
execute_command("FLUSHDB\r\n") == OK
end
def flush_all
ensure_retry do
puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
trap('INT') {quit; return false}
sleep 5
write "FLUSHALL\r\n"
get_response == OK
end
puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
trap('INT') {quit; return false}
sleep 5
execute_command("FLUSHALL\r\n") == OK
end
def last_save
write "LASTSAVE\r\n"
get_response.to_i
execute_command("LASTSAVE\r\n").to_i
end
def bgsave
write "BGSAVE\r\n"
get_response == OK
execute_command("BGSAVE\r\n") == OK
end
def info
info = {}
write("INFO\r\n")
x = get_response
x.each do |kv|
x = execute_command("INFO\r\n")
x.each_line do |kv|
k,v = kv.split(':', 2)
k,v = k.chomp, v = v.chomp
info[k.to_sym] = v
......@@ -121,55 +87,16 @@ class Redis
info
end
def bulk_reply
begin
x = read
puts "bulk_reply read value is #{x.inspect}" if $debug
return x
rescue => e
puts "error in bulk_reply #{e}" if $debug
nil
end
end
def write(data)
with_socket_management(@server) do |socket|
puts "writing: #{data}" if $debug
socket.write(data)
end
end
def fetch(len)
with_socket_management(@server) do |socket|
len = [0, len.to_i].max
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
res
end
end
def keys(glob)
write "KEYS #{glob}\r\n"
get_response.split(' ')
execute_command("KEYS #{glob}\r\n").split(' ')
end
def rename!(oldkey, newkey)
write "RENAME #{oldkey} #{newkey}\r\n"
get_response
execute_command("RENAME #{oldkey} #{newkey}\r\n")
end
def rename(oldkey, newkey)
write "RENAMENX #{oldkey} #{newkey}\r\n"
case get_response
case execute_command("RENAMENX #{oldkey} #{newkey}\r\n")
when -1
raise RedisRenameError, "source key: #{oldkey} does not exist"
when 0
......@@ -182,13 +109,11 @@ class Redis
end
def key?(key)
write "EXISTS #{key}\r\n"
get_response == 1
execute_command("EXISTS #{key}\r\n") == 1
end
def delete(key)
write "DEL #{key}\r\n"
get_response == 1
execute_command("DEL #{key}\r\n") == 1
end
def [](key)
......@@ -196,41 +121,35 @@ class Redis
end
def get(key)
write "GET #{key}\r\n"
get_response
execute_command("GET #{key}\r\n")
end
def mget(*keys)
write "MGET #{keys.join(' ')}\r\n"
get_response
execute_command("MGET #{keys.join(' ')}\r\n")
end
def incr(key, increment=nil)
if increment
write "INCRBY #{key} #{increment}\r\n"
execute_command("INCRBY #{key} #{increment}\r\n")
else
write "INCR #{key}\r\n"
execute_command("INCR #{key}\r\n")
end
get_response
end
def decr(key, decrement=nil)
if decrement
write "DECRBY #{key} #{decrement}\r\n"
execute_command("DECRBY #{key} #{decrement}\r\n")
else
write "DECR #{key}\r\n"
execute_command("DECR #{key}\r\n")
end
get_response
end
def randkey
write "RANDOMKEY\r\n"
get_response
execute_command("RANDOMKEY\r\n")
end
def list_length(key)
write "LLEN #{key}\r\n"
case i = get_response
case i = execute_command("LLEN #{key}\r\n")
when -2
raise RedisError, "key: #{key} does not hold a list value"
else
......@@ -239,53 +158,43 @@ class Redis
end
def type?(key)
write "TYPE #{key}\r\n"
get_response
execute_command("TYPE #{key}\r\n")
end
def push_tail(key, string)
write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
get_response
def push_tail(key, val)
execute_command("RPUSH #{key} #{value_to_wire(val)}\r\n")
end
def push_head(key, string)
write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
get_response
def push_head(key, val)
execute_command("LPUSH #{key} #{value_to_wire(val)}\r\n")
end
def pop_head(key)
write "LPOP #{key}\r\n"
get_response
execute_command("LPOP #{key}\r\n")
end
def pop_tail(key)
write "RPOP #{key}\r\n"
get_response
execute_command("RPOP #{key}\r\n")
end
def list_set(key, index, val)
write "LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n"
get_response == OK
execute_command("LSET #{key} #{index} #{value_to_wire(val)}\r\n") == OK
end
def list_range(key, start, ending)
write "LRANGE #{key} #{start} #{ending}\r\n"
get_response
execute_command("LRANGE #{key} #{start} #{ending}\r\n")
end
def list_trim(key, start, ending)
write "LTRIM #{key} #{start} #{ending}\r\n"
get_response
execute_command("LTRIM #{key} #{start} #{ending}\r\n")
end
def list_index(key, index)
write "LINDEX #{key} #{index}\r\n"
get_response
execute_command("LINDEX #{key} #{index}\r\n")
end
def list_rm(key, count, value)
write "LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n"
case num = get_response
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
......@@ -296,8 +205,7 @@ class Redis
end
def set_add(key, member)
write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
case get_response
case execute_command("SADD #{key} #{value_to_wire(member)}\r\n")
when 1
true
when 0
......@@ -308,8 +216,7 @@ class Redis
end
def set_delete(key, member)
write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
case get_response
case execute_command("SREM #{key} #{value_to_wire(member)}\r\n")
when 1
true
when 0
......@@ -320,8 +227,7 @@ class Redis
end
def set_count(key)
write "SCARD #{key}\r\n"
case i = get_response
case i = execute_command("SCARD #{key}\r\n")
when -2
raise RedisError, "key: #{key} contains a non set value"
else
......@@ -330,8 +236,7 @@ class Redis
end
def set_member?(key, member)
write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
case get_response
case execute_command("SISMEMBER #{key} #{value_to_wire(member)}\r\n")
when 1
true
when 0
......@@ -342,57 +247,93 @@ class Redis
end
def set_members(key)
write "SMEMBERS #{key}\r\n"
Set.new(get_response)
Set.new(execute_command("SMEMBERS #{key}\r\n"))
end
def set_intersect(*keys)
write "SINTER #{keys.join(' ')}\r\n"
Set.new(get_response)
Set.new(execute_command("SINTER #{keys.join(' ')}\r\n"))
end
def set_inter_store(destkey, *keys)
write "SINTERSTORE #{destkey} #{keys.join(' ')}\r\n"
get_response
execute_command("SINTERSTORE #{destkey} #{keys.join(' ')}\r\n")
end
def set_union(*keys)
write "SUNION #{keys.join(' ')}\r\n"
Set.new(get_response)
Set.new(execute_command("SUNION #{keys.join(' ')}\r\n"))
end
def set_union_store(destkey, *keys)
write "SUNIONSTORE #{destkey} #{keys.join(' ')}\r\n"
get_response
execute_command("SUNIONSTORE #{destkey} #{keys.join(' ')}\r\n")
end
def set_diff(*keys)
write "SDIFF #{keys.join(' ')}\r\n"
Set.new(get_response)
Set.new(execute_command("SDIFF #{keys.join(' ')}\r\n"))
end
def set_diff_store(destkey, *keys)
write "SDIFFSTORE #{destkey} #{keys.join(' ')}\r\n"
get_response
execute_command("SDIFFSTORE #{destkey} #{keys.join(' ')}\r\n")
end
def set_move(srckey, destkey, member)
write "SMOVE #{srckey} #{destkey} #{member.to_s.size}\r\n#{member}\r\n"
get_response == 1
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]}" if opts[:get]
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"
write(cmd)
get_response
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
begin
x = read
puts "bulk_reply read value is #{x.inspect}" if $debug
return x
rescue => e
puts "error in bulk_reply #{e}" if $debug
nil
end
end
def write(data)
puts "writing: #{data}" if $debug
@socket.write(data)
end
def read(length = 0)
length = read_proto unless length > 0
res = @socket.read(length)
puts "read is #{res.inspect}" if $debug
res
end
def multi_bulk
......@@ -418,28 +359,6 @@ class Redis
r
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
begin
res = read_proto
......@@ -452,13 +371,26 @@ class Redis
raise RedisError
end
end
def execute_command(command, ignore_response=false)
ss = server.socket
unless ss.object_id == @socket.object_id
@socket = ss
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
write(command)
get_response unless ignore_response
rescue Errno::ECONNRESET, Errno::EPIPE, NoMethodError, Timeout::Error => e
raise RedisError, "Connection error"
end
def get_response
begin
rtype = get_reply
rescue => e
raise RedisError, e.inspect
end
rtype = get_reply
puts "reply_type is #{rtype.inspect}" if $debug
case rtype
when SINGLE
......@@ -512,13 +444,21 @@ class Redis
end
def read_proto
with_socket_management(@server) do |socket|
if res = socket.gets
x = res.chomp
puts "read_proto is #{x.inspect}\n\n" if $debug
x.to_i
end
res = @socket.readline
x = res.chomp
puts "read_proto is #{x.inspect}\n\n" if $debug
x.to_i
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
"#{value_size}\r\n#{value_str}"
end
end
......@@ -24,12 +24,6 @@ end
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.
......@@ -40,16 +34,6 @@ class Server
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.
......@@ -67,7 +51,6 @@ class Server
@port = port.to_i
@sock = nil
@retry = nil
@status = 'NOT CONNECTED'
@timeout = timeout
end
......@@ -83,38 +66,31 @@ class Server
# Returns the connected socket object on success or nil on failure.
def socket
return @sock if @sock and not @sock.closed?
@sock = nil
# If the host was dead, don't retry for a while.
return if @retry and @retry > Time.now
return @sock if socket_alive?
close
# Attempt to connect if not already connected.
begin
@sock = connect_to(@host, @port, @timeout)
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
@retry = nil
@status = 'CONNECTED'
rescue Errno::EPIPE, Errno::ECONNREFUSED => e
puts "Socket died... socket: #{@sock.inspect}\n" if $debug
@sock.close
puts "Socket died... : #{e}\n" if $debug
retry
rescue SocketError, SystemCallError, IOError => err
puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
mark_dead err
end
@sock
end
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
socket.instance_eval <<-EOR
alias :blocking_gets :gets
def gets(*args)
alias :blocking_readline :readline
def readline(*args)
RedisTimer.timeout(#{timeout}) do
self.blocking_gets(*args)
self.blocking_readline(*args)
end
end
alias :blocking_read :read
......@@ -134,27 +110,22 @@ class Server
socket
end
##
# Close the connection to the redis server targeted by this
# object. The server is not considered dead.
# object.
def close
@sock.close if @sock && !@sock.closed?
@sock.close if !@sock.nil? && !@sock.closed?
@sock = nil
@retry = nil
@status = "NOT CONNECTED"
end
##
# Mark the server as dead and close its socket.
def mark_dead(error)
@sock.close if @sock && !@sock.closed?
@sock = nil
@retry = Time.now #+ RETRY_DELAY
reason = "#{error.class.name}: #{error.message}"
@status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
puts @status
end
private
def socket_alive?
#BTM - TODO - FileStat is borked under JRuby
unless defined?(JRUBY_VERSION)
!@sock.nil? && !@sock.closed? && @sock.stat.readable?
else
!@sock.nil? && !@sock.closed?
end
end
end
......@@ -2,7 +2,7 @@
Gem::Specification.new do |s|
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.authors = ["Ezra Zygmuntowicz", "Taylor Weibley", "Matthew Clark"]
......
......@@ -13,8 +13,8 @@ end
describe "redis" do
before(:all) do
@r = Redis.new
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
# use database 15 for testing so we dont accidentally step on you real data
@r = Redis.new :db => 15
end
before(:each) do
......@@ -29,6 +29,9 @@ describe "redis" do
@r.quit
end
it 'should be able to PING' do
@r.ping.should == true
end
it "should be able to GET a key" do
@r['foo'].should == 'bar'
......@@ -102,7 +105,16 @@ describe "redis" do
lambda {@r.rename 'foo', 'bar'}.should raise_error(RedisRenameError)
@r['bar'].should == 'ohai'
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
@r['foo'] = 'bar'
@r.expire('foo', 1)
......@@ -287,6 +299,7 @@ describe "redis" do
@r.set_inter_store('newone', 'set', 'set2').should == 'OK'
@r.set_members('newone').should == Set.new(['key2'])
@r.delete('set')
@r.delete('set2')
end
#
it "should be able to do set union" do
......@@ -296,6 +309,7 @@ describe "redis" do
@r.set_add "set2", 'key3'
@r.set_union('set', 'set2').should == Set.new(['key1','key2','key3'])
@r.delete('set')
@r.delete('set2')
end
#
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_members('newone').should == Set.new(['key1','key2','key3'])
@r.delete('set')
@r.delete('set2')
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 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
#
it "should be able move elements from one set to another" do
@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], :order => 'desc alpha').should == ['taj']
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
[: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.delete('list')
end
it "should select db on connection"
it "should re-select db on reconnection"
end
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
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
%w(redis-benchmark redis-cli redis-server).each do |bin|
sh "sudo cp /tmp/redis/#{bin} /usr/bin/"
......
......@@ -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";
/* 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);
return;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册