ezmobius-redis 0.0.3.4 → 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +5 -2
- data/Rakefile +13 -10
- data/lib/dist_redis.rb +13 -6
- data/lib/pipeline.rb +7 -16
- data/lib/redis.rb +240 -468
- data/spec/redis_spec.rb +262 -218
- data/spec/spec_helper.rb +1 -1
- metadata +7 -5
- data/lib/server.rb +0 -160
data/README.markdown
CHANGED
@@ -12,7 +12,10 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
|
|
12
12
|
|
13
13
|
## Dependencies
|
14
14
|
|
15
|
-
1.
|
15
|
+
1. rspec -
|
16
|
+
sudo gem install rspec
|
17
|
+
|
18
|
+
2. redis -
|
16
19
|
|
17
20
|
rake redis:install
|
18
21
|
|
@@ -20,7 +23,7 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
|
|
20
23
|
|
21
24
|
rake dtach:install
|
22
25
|
|
23
|
-
3.
|
26
|
+
3. git - git is the new black.
|
24
27
|
|
25
28
|
## Setup
|
26
29
|
|
data/Rakefile
CHANGED
@@ -8,10 +8,10 @@ require 'tasks/redis.tasks'
|
|
8
8
|
|
9
9
|
GEM = 'redis'
|
10
10
|
GEM_NAME = 'redis'
|
11
|
-
GEM_VERSION = '0.
|
12
|
-
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark']
|
13
|
-
EMAIL = "
|
14
|
-
HOMEPAGE = "http://github.com/
|
11
|
+
GEM_VERSION = '0.1'
|
12
|
+
AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark', 'Brian McKinney', 'Salvatore Sanfilippo', 'Luca Guidi']
|
13
|
+
EMAIL = "ez@engineyard.com"
|
14
|
+
HOMEPAGE = "http://github.com/ezmobius/redis-rb"
|
15
15
|
SUMMARY = "Ruby client library for redis key value storage server"
|
16
16
|
|
17
17
|
spec = Gem::Specification.new do |s|
|
@@ -25,13 +25,10 @@ spec = Gem::Specification.new do |s|
|
|
25
25
|
s.authors = AUTHORS
|
26
26
|
s.email = EMAIL
|
27
27
|
s.homepage = HOMEPAGE
|
28
|
-
|
29
|
-
# Uncomment this to add a dependency
|
30
|
-
# s.add_dependency "foo"
|
31
|
-
|
28
|
+
s.add_dependency "rspec"
|
32
29
|
s.require_path = 'lib'
|
33
30
|
s.autorequire = GEM
|
34
|
-
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec}/**/*")
|
31
|
+
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,tasks,spec}/**/*")
|
35
32
|
end
|
36
33
|
|
37
34
|
task :default => :spec
|
@@ -56,4 +53,10 @@ task :make_spec do
|
|
56
53
|
File.open("#{GEM}.gemspec", "w") do |file|
|
57
54
|
file.puts spec.to_ruby
|
58
55
|
end
|
59
|
-
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Run all examples with RCov"
|
59
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
60
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
61
|
+
t.rcov = true
|
62
|
+
end
|
data/lib/dist_redis.rb
CHANGED
@@ -2,13 +2,20 @@ require 'redis'
|
|
2
2
|
require 'hash_ring'
|
3
3
|
class DistRedis
|
4
4
|
attr_reader :ring
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
def initialize(opts={})
|
6
|
+
hosts = []
|
7
|
+
|
8
|
+
db = opts[:db] || nil
|
9
|
+
timeout = opts[:timeout] || nil
|
10
|
+
|
11
|
+
raise Error, "No hosts given" unless opts[:hosts]
|
12
|
+
|
13
|
+
opts[:hosts].each do |h|
|
14
|
+
host, port = h.split(':')
|
15
|
+
hosts << Redis.new(:host => host, :port => port, :db => db, :timeout => timeout, :db => db)
|
10
16
|
end
|
11
|
-
|
17
|
+
|
18
|
+
@ring = HashRing.new hosts
|
12
19
|
end
|
13
20
|
|
14
21
|
def node_for_key(key)
|
data/lib/pipeline.rb
CHANGED
@@ -8,24 +8,15 @@ class Redis
|
|
8
8
|
@redis = redis
|
9
9
|
@commands = []
|
10
10
|
end
|
11
|
-
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
def write(data)
|
16
|
-
@commands << data
|
17
|
-
write_and_read if @commands.size >= BUFFER_SIZE
|
11
|
+
|
12
|
+
def call_command(command)
|
13
|
+
@commands << command
|
18
14
|
end
|
19
|
-
|
20
|
-
def
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def write_and_read
|
25
|
-
@redis.write @commands.join
|
26
|
-
@redis.read_socket
|
15
|
+
|
16
|
+
def execute
|
17
|
+
@redis.call_command(@commands)
|
27
18
|
@commands.clear
|
28
19
|
end
|
29
20
|
|
30
21
|
end
|
31
|
-
end
|
22
|
+
end
|
data/lib/redis.rb
CHANGED
@@ -1,527 +1,299 @@
|
|
1
1
|
require 'socket'
|
2
|
-
require 'set'
|
3
|
-
require File.join(File.dirname(__FILE__),'server')
|
4
2
|
require File.join(File.dirname(__FILE__),'pipeline')
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
begin
|
5
|
+
if RUBY_VERSION >= '1.9'
|
6
|
+
require 'timeout'
|
7
|
+
RedisTimer = Timeout
|
8
|
+
else
|
9
|
+
require 'system_timer'
|
10
|
+
RedisTimer = SystemTimer
|
11
|
+
end
|
12
|
+
rescue LoadError
|
13
|
+
RedisTimer = nil
|
10
14
|
end
|
15
|
+
|
11
16
|
class Redis
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
17
|
+
OK = "OK".freeze
|
18
|
+
MINUS = "-".freeze
|
19
|
+
PLUS = "+".freeze
|
20
|
+
COLON = ":".freeze
|
21
|
+
DOLLAR = "$".freeze
|
22
|
+
ASTERISK = "*".freeze
|
23
|
+
|
24
|
+
BULK_COMMANDS = {
|
25
|
+
"set" => true,
|
26
|
+
"setnx" => true,
|
27
|
+
"rpush" => true,
|
28
|
+
"lpush" => true,
|
29
|
+
"lset" => true,
|
30
|
+
"lrem" => true,
|
31
|
+
"sadd" => true,
|
32
|
+
"srem" => true,
|
33
|
+
"sismember" => true,
|
34
|
+
"echo" => true,
|
35
|
+
"getset" => true,
|
36
|
+
"smove" => true
|
37
|
+
}
|
38
|
+
|
39
|
+
BOOLEAN_PROCESSOR = lambda{|r| r == 0 ? false : r}
|
40
|
+
|
41
|
+
REPLY_PROCESSOR = {
|
42
|
+
"exists" => BOOLEAN_PROCESSOR,
|
43
|
+
"sismember" => BOOLEAN_PROCESSOR,
|
44
|
+
"sadd" => BOOLEAN_PROCESSOR,
|
45
|
+
"srem" => BOOLEAN_PROCESSOR,
|
46
|
+
"smove" => BOOLEAN_PROCESSOR,
|
47
|
+
"move" => BOOLEAN_PROCESSOR,
|
48
|
+
"setnx" => BOOLEAN_PROCESSOR,
|
49
|
+
"del" => BOOLEAN_PROCESSOR,
|
50
|
+
"renamenx" => BOOLEAN_PROCESSOR,
|
51
|
+
"expire" => BOOLEAN_PROCESSOR,
|
52
|
+
"keys" => lambda{|r| r.split(" ")},
|
53
|
+
"info" => lambda{|r|
|
54
|
+
info = {}
|
55
|
+
r.each_line {|kv|
|
56
|
+
k,v = kv.split(":",2).map{|x| x.chomp}
|
57
|
+
info[k.to_sym] = v
|
58
|
+
}
|
59
|
+
info
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
ALIASES = {
|
64
|
+
"flush_db" => "flushdb",
|
65
|
+
"flush_all" => "flushall",
|
66
|
+
"last_save" => "lastsave",
|
67
|
+
"key?" => "exists",
|
68
|
+
"delete" => "del",
|
69
|
+
"randkey" => "randomkey",
|
70
|
+
"list_length" => "llen",
|
71
|
+
"push_tail" => "rpush",
|
72
|
+
"push_head" => "lpush",
|
73
|
+
"pop_tail" => "rpop",
|
74
|
+
"pop_head" => "lpop",
|
75
|
+
"list_set" => "lset",
|
76
|
+
"list_range" => "lrange",
|
77
|
+
"list_trim" => "ltrim",
|
78
|
+
"list_index" => "lindex",
|
79
|
+
"list_rm" => "lrem",
|
80
|
+
"set_add" => "sadd",
|
81
|
+
"set_delete" => "srem",
|
82
|
+
"set_count" => "scard",
|
83
|
+
"set_member?" => "sismember",
|
84
|
+
"set_members" => "smembers",
|
85
|
+
"set_intersect" => "sinter",
|
86
|
+
"set_intersect_store" => "sinterstore",
|
87
|
+
"set_inter_store" => "sinterstore",
|
88
|
+
"set_union" => "sunion",
|
89
|
+
"set_union_store" => "sunionstore",
|
90
|
+
"set_diff" => "sdiff",
|
91
|
+
"set_diff_store" => "sdiffstore",
|
92
|
+
"set_move" => "smove",
|
93
|
+
"set_unless_exists" => "setnx",
|
94
|
+
"rename_unless_exists" => "renamenx",
|
95
|
+
"type?" => "type"
|
96
|
+
}
|
97
|
+
|
98
|
+
DISABLED_COMMANDS = {
|
99
|
+
"monitor" => true,
|
100
|
+
"sync" => true
|
101
|
+
}
|
102
|
+
|
103
|
+
def initialize(options = {})
|
104
|
+
@host = options[:host] || '127.0.0.1'
|
105
|
+
@port = (options[:port] || 6379).to_i
|
106
|
+
@db = (options[:db] || 0).to_i
|
107
|
+
@timeout = (options[:timeout] || 5).to_i
|
108
|
+
$debug = options[:debug]
|
109
|
+
@password = options[:password]
|
110
|
+
connect_to_server
|
62
111
|
end
|
63
112
|
|
64
|
-
def
|
65
|
-
|
66
|
-
trap("INT") { puts "\nGot ^C! Dying!"; exit }
|
67
|
-
write "MONITOR\r\n"
|
68
|
-
puts "Now Monitoring..."
|
69
|
-
socket.read(12)
|
70
|
-
loop do
|
71
|
-
x = socket.gets
|
72
|
-
puts x unless x.nil?
|
73
|
-
end
|
74
|
-
end
|
113
|
+
def to_s
|
114
|
+
"Redis Client connected to #{@host}:#{@port} against DB #{@db}"
|
75
115
|
end
|
76
116
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
def select_db(index)
|
82
|
-
@db = index
|
83
|
-
write "SELECT #{index}\r\n"
|
84
|
-
get_response
|
85
|
-
end
|
86
|
-
|
87
|
-
def flush_db
|
88
|
-
write "FLUSHDB\r\n"
|
89
|
-
get_response == OK
|
90
|
-
end
|
91
|
-
|
92
|
-
def flush_all
|
93
|
-
puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
|
94
|
-
trap('INT') {quit; return false}
|
95
|
-
sleep 5
|
96
|
-
write "FLUSHALL\r\n"
|
97
|
-
get_response == OK
|
117
|
+
def connect_to_server
|
118
|
+
@sock = connect_to(@host, @port, @timeout == 0 ? nil : @timeout)
|
119
|
+
call_command(["auth",@password]) if @password
|
120
|
+
call_command(["select",@db]) unless @db == 0
|
98
121
|
end
|
99
122
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
x = get_response
|
114
|
-
x.each do |kv|
|
115
|
-
k,v = kv.split(':', 2)
|
116
|
-
k,v = k.chomp, v = v.chomp
|
117
|
-
info[k.to_sym] = v
|
118
|
-
end
|
119
|
-
info
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
def bulk_reply
|
124
|
-
begin
|
125
|
-
x = read
|
126
|
-
puts "bulk_reply read value is #{x.inspect}" if $debug
|
127
|
-
return x
|
128
|
-
rescue => e
|
129
|
-
puts "error in bulk_reply #{e}" if $debug
|
130
|
-
nil
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def write(data)
|
135
|
-
with_socket_management(@server) do |socket|
|
136
|
-
puts "writing: #{data}" if $debug
|
137
|
-
socket.write(data)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def fetch(len)
|
142
|
-
with_socket_management(@server) do |socket|
|
143
|
-
len = [0, len.to_i].max
|
144
|
-
res = socket.read(len + 2)
|
145
|
-
res = res.chomp if res
|
146
|
-
res
|
123
|
+
def connect_to(host, port, timeout=nil)
|
124
|
+
# We support connect() timeout only if system_timer is availabe
|
125
|
+
# or if we are running against Ruby >= 1.9
|
126
|
+
# Timeout reading from the socket instead will be supported anyway.
|
127
|
+
if @timeout != 0 and RedisTimer
|
128
|
+
begin
|
129
|
+
sock = TCPSocket.new(host, port)
|
130
|
+
rescue Timeout::Error
|
131
|
+
@sock = nil
|
132
|
+
raise Timeout::Error, "Timeout connecting to the server"
|
133
|
+
end
|
134
|
+
else
|
135
|
+
sock = TCPSocket.new(host, port)
|
147
136
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
137
|
+
sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
138
|
+
|
139
|
+
# If the timeout is set we set the low level socket options in order
|
140
|
+
# to make sure a blocking read will return after the specified number
|
141
|
+
# of seconds. This hack is from memcached ruby client.
|
142
|
+
if timeout
|
143
|
+
secs = Integer(timeout)
|
144
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
145
|
+
optval = [secs, usecs].pack("l_2")
|
146
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
147
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
155
148
|
end
|
149
|
+
sock
|
156
150
|
end
|
157
151
|
|
158
|
-
def
|
159
|
-
|
160
|
-
get_response.split(' ')
|
152
|
+
def method_missing(*argv)
|
153
|
+
call_command(argv)
|
161
154
|
end
|
162
155
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
raise RedisRenameError, "source and destination keys are the same"
|
177
|
-
when 1
|
178
|
-
true
|
156
|
+
def call_command(argv)
|
157
|
+
puts argv.inspect if $debug
|
158
|
+
# this wrapper to raw_call_command handle reconnection on socket
|
159
|
+
# error. We try to reconnect just one time, otherwise let the error
|
160
|
+
# araise.
|
161
|
+
connect_to_server if !@sock
|
162
|
+
begin
|
163
|
+
raw_call_command(argv.dup)
|
164
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
165
|
+
@sock.close
|
166
|
+
@sock = nil
|
167
|
+
connect_to_server
|
168
|
+
raw_call_command(argv.dup)
|
179
169
|
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def key?(key)
|
183
|
-
write "EXISTS #{key}\r\n"
|
184
|
-
get_response == 1
|
185
|
-
end
|
186
|
-
|
187
|
-
def delete(key)
|
188
|
-
write "DEL #{key}\r\n"
|
189
|
-
get_response == 1
|
190
|
-
end
|
191
|
-
|
192
|
-
def [](key)
|
193
|
-
get(key)
|
194
170
|
end
|
195
171
|
|
196
|
-
def
|
197
|
-
|
198
|
-
get_response
|
199
|
-
end
|
200
|
-
|
201
|
-
def mget(*keys)
|
202
|
-
write "MGET #{keys.join(' ')}\r\n"
|
203
|
-
get_response
|
204
|
-
end
|
172
|
+
def raw_call_command(argvp)
|
173
|
+
pipeline = argvp[0].is_a?(Array)
|
205
174
|
|
206
|
-
|
207
|
-
|
208
|
-
write "INCRBY #{key} #{increment}\r\n"
|
175
|
+
unless pipeline
|
176
|
+
argvv = [argvp]
|
209
177
|
else
|
210
|
-
|
211
|
-
end
|
212
|
-
get_response
|
213
|
-
end
|
214
|
-
|
215
|
-
def decr(key, decrement=nil)
|
216
|
-
if decrement
|
217
|
-
write "DECRBY #{key} #{decrement}\r\n"
|
218
|
-
else
|
219
|
-
write "DECR #{key}\r\n"
|
220
|
-
end
|
221
|
-
get_response
|
222
|
-
end
|
223
|
-
|
224
|
-
def randkey
|
225
|
-
write "RANDOMKEY\r\n"
|
226
|
-
get_response
|
227
|
-
end
|
228
|
-
|
229
|
-
def list_length(key)
|
230
|
-
write "LLEN #{key}\r\n"
|
231
|
-
case i = get_response
|
232
|
-
when -2
|
233
|
-
raise RedisError, "key: #{key} does not hold a list value"
|
234
|
-
else
|
235
|
-
i
|
178
|
+
argvv = argvp
|
236
179
|
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def type?(key)
|
240
|
-
write "TYPE #{key}\r\n"
|
241
|
-
get_response
|
242
|
-
end
|
243
|
-
|
244
|
-
def push_tail(key, string)
|
245
|
-
write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
|
246
|
-
get_response
|
247
|
-
end
|
248
|
-
|
249
|
-
def push_head(key, string)
|
250
|
-
write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
|
251
|
-
get_response
|
252
|
-
end
|
253
|
-
|
254
|
-
def pop_head(key)
|
255
|
-
write "LPOP #{key}\r\n"
|
256
|
-
get_response
|
257
|
-
end
|
258
180
|
|
259
|
-
|
260
|
-
write "RPOP #{key}\r\n"
|
261
|
-
get_response
|
262
|
-
end
|
181
|
+
command = ''
|
263
182
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
write "LTRIM #{key} #{start} #{ending}\r\n"
|
276
|
-
get_response
|
277
|
-
end
|
278
|
-
|
279
|
-
def list_index(key, index)
|
280
|
-
write "LINDEX #{key} #{index}\r\n"
|
281
|
-
get_response
|
282
|
-
end
|
283
|
-
|
284
|
-
def list_rm(key, count, value)
|
285
|
-
write "LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n"
|
286
|
-
case num = get_response
|
287
|
-
when -1
|
288
|
-
raise RedisError, "key: #{key} does not exist"
|
289
|
-
when -2
|
290
|
-
raise RedisError, "key: #{key} does not hold a list value"
|
291
|
-
else
|
292
|
-
num
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
def set_add(key, member)
|
297
|
-
write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
298
|
-
case get_response
|
299
|
-
when 1
|
300
|
-
true
|
301
|
-
when 0
|
302
|
-
false
|
303
|
-
when -2
|
304
|
-
raise RedisError, "key: #{key} contains a non set value"
|
183
|
+
argvv.each do |argv|
|
184
|
+
bulk = nil
|
185
|
+
argv[0] = argv[0].to_s.downcase
|
186
|
+
argv[0] = ALIASES[argv[0]] if ALIASES[argv[0]]
|
187
|
+
raise "#{argv[0]} command is disabled" if DISABLED_COMMANDS[argv[0]]
|
188
|
+
if BULK_COMMANDS[argv[0]] and argv.length > 1
|
189
|
+
bulk = argv[-1].to_s
|
190
|
+
argv[-1] = bulk.length
|
191
|
+
end
|
192
|
+
command << argv.join(' ') + "\r\n"
|
193
|
+
command << bulk + "\r\n" if bulk
|
305
194
|
end
|
306
|
-
end
|
307
195
|
|
308
|
-
|
309
|
-
write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
310
|
-
case get_response
|
311
|
-
when 1
|
312
|
-
true
|
313
|
-
when 0
|
314
|
-
false
|
315
|
-
when -2
|
316
|
-
raise RedisError, "key: #{key} contains a non set value"
|
317
|
-
end
|
318
|
-
end
|
196
|
+
@sock.write(command)
|
319
197
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
when -2
|
324
|
-
raise RedisError, "key: #{key} contains a non set value"
|
325
|
-
else
|
326
|
-
i
|
198
|
+
results = argvv.map do |argv|
|
199
|
+
processor = REPLY_PROCESSOR[argv[0]]
|
200
|
+
processor ? processor.call(read_reply) : read_reply
|
327
201
|
end
|
328
|
-
end
|
329
202
|
|
330
|
-
|
331
|
-
write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
|
332
|
-
case get_response
|
333
|
-
when 1
|
334
|
-
true
|
335
|
-
when 0
|
336
|
-
false
|
337
|
-
when -2
|
338
|
-
raise RedisError, "key: #{key} contains a non set value"
|
339
|
-
end
|
203
|
+
return pipeline ? results : results[0]
|
340
204
|
end
|
341
205
|
|
342
|
-
def
|
343
|
-
|
344
|
-
Set.new(get_response)
|
206
|
+
def select(*args)
|
207
|
+
raise "SELECT not allowed, use the :db option when creating the object"
|
345
208
|
end
|
346
209
|
|
347
|
-
def
|
348
|
-
|
349
|
-
Set.new(get_response)
|
210
|
+
def [](key)
|
211
|
+
self.get(key)
|
350
212
|
end
|
351
213
|
|
352
|
-
def
|
353
|
-
|
354
|
-
get_response
|
355
|
-
end
|
356
|
-
|
357
|
-
def set_union(*keys)
|
358
|
-
write "SUNION #{keys.join(' ')}\r\n"
|
359
|
-
Set.new(get_response)
|
214
|
+
def []=(key,value)
|
215
|
+
set(key,value)
|
360
216
|
end
|
361
217
|
|
362
|
-
def
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
def set_diff(*keys)
|
368
|
-
write "SDIFF #{keys.join(' ')}\r\n"
|
369
|
-
Set.new(get_response)
|
218
|
+
def set(key, value, expiry=nil)
|
219
|
+
s = call_command([:set, key, value]) == OK
|
220
|
+
expire(key, expiry) if s && expiry
|
221
|
+
s
|
370
222
|
end
|
371
223
|
|
372
|
-
def
|
373
|
-
|
374
|
-
|
224
|
+
def sort(key, options = {})
|
225
|
+
cmd = ["SORT"]
|
226
|
+
cmd << key
|
227
|
+
cmd << "BY #{options[:by]}" if options[:by]
|
228
|
+
cmd << "GET #{[options[:get]].flatten * ' GET '}" if options[:get]
|
229
|
+
cmd << "#{options[:order]}" if options[:order]
|
230
|
+
cmd << "LIMIT #{options[:limit].join(' ')}" if options[:limit]
|
231
|
+
call_command(cmd)
|
375
232
|
end
|
376
233
|
|
377
|
-
def
|
378
|
-
|
379
|
-
get_response == 1
|
234
|
+
def incr(key, increment = nil)
|
235
|
+
call_command(increment ? ["incrby",key,increment] : ["incr",key])
|
380
236
|
end
|
381
237
|
|
382
|
-
def
|
383
|
-
|
384
|
-
cmd << " BY #{opts[:by]}" if opts[:by]
|
385
|
-
cmd << " GET #{opts[:get]}" if opts[:get]
|
386
|
-
cmd << " INCR #{opts[:incr]}" if opts[:incr]
|
387
|
-
cmd << " DEL #{opts[:del]}" if opts[:del]
|
388
|
-
cmd << " DECR #{opts[:decr]}" if opts[:decr]
|
389
|
-
cmd << " #{opts[:order]}" if opts[:order]
|
390
|
-
cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
|
391
|
-
cmd << "\r\n"
|
392
|
-
write(cmd)
|
393
|
-
get_response
|
394
|
-
end
|
395
|
-
|
396
|
-
def multi_bulk
|
397
|
-
res = read_proto
|
398
|
-
puts "mb res is #{res.inspect}" if $debug
|
399
|
-
list = []
|
400
|
-
Integer(res).times do
|
401
|
-
vf = get_response
|
402
|
-
puts "curren vf is #{vf.inspect}" if $debug
|
403
|
-
list << vf
|
404
|
-
puts "current list is #{list.inspect}" if $debug
|
405
|
-
end
|
406
|
-
list
|
238
|
+
def decr(key,decrement = nil)
|
239
|
+
call_command(decrement ? ["decrby",key,decrement] : ["decr",key])
|
407
240
|
end
|
408
|
-
|
409
|
-
def get_reply
|
410
|
-
begin
|
411
|
-
r = read(1)
|
412
|
-
raise RedisError if (r == "\r" || r == "\n")
|
413
|
-
rescue RedisError
|
414
|
-
retry
|
415
|
-
end
|
416
|
-
r
|
417
|
-
end
|
418
|
-
|
419
|
-
def []=(key, val)
|
420
|
-
set(key,val)
|
421
|
-
end
|
422
|
-
|
423
241
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
s
|
242
|
+
# Ruby defines a now deprecated type method so we need to override it here
|
243
|
+
# since it will never hit method_missing
|
244
|
+
def type(key)
|
245
|
+
call_command(['type', key])
|
429
246
|
end
|
430
247
|
|
431
|
-
def
|
432
|
-
|
433
|
-
|
248
|
+
def quit
|
249
|
+
call_command(['quit'])
|
250
|
+
rescue Errno::ECONNRESET
|
434
251
|
end
|
435
252
|
|
436
|
-
def
|
437
|
-
|
438
|
-
|
253
|
+
def pipelined(&block)
|
254
|
+
pipeline = Pipeline.new self
|
255
|
+
yield pipeline
|
256
|
+
pipeline.execute
|
439
257
|
end
|
440
258
|
|
441
|
-
def
|
442
|
-
|
443
|
-
|
444
|
-
end
|
445
|
-
|
446
|
-
def status_code_reply
|
447
|
-
begin
|
448
|
-
res = read_proto
|
449
|
-
if res.index('-') == 0
|
450
|
-
raise RedisError, res
|
451
|
-
else
|
452
|
-
true
|
453
|
-
end
|
454
|
-
rescue RedisError
|
455
|
-
raise RedisError
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
def get_response
|
259
|
+
def read_reply
|
260
|
+
# We read the first byte using read() mainly because gets() is
|
261
|
+
# immune to raw socket timeouts.
|
460
262
|
begin
|
461
|
-
rtype =
|
462
|
-
rescue
|
463
|
-
|
263
|
+
rtype = @sock.read(1)
|
264
|
+
rescue Errno::EAGAIN
|
265
|
+
# We want to make sure it reconnects on the next command after the
|
266
|
+
# timeout. Otherwise the server may reply in the meantime leaving
|
267
|
+
# the protocol in a desync status.
|
268
|
+
@sock = nil
|
269
|
+
raise Errno::EAGAIN, "Timeout reading from the socket"
|
464
270
|
end
|
465
|
-
|
271
|
+
|
272
|
+
raise Errno::ECONNRESET,"Connection lost" if !rtype
|
273
|
+
line = @sock.gets
|
466
274
|
case rtype
|
467
|
-
when
|
468
|
-
|
469
|
-
when
|
470
|
-
|
471
|
-
when
|
472
|
-
|
473
|
-
when
|
474
|
-
|
475
|
-
|
476
|
-
|
275
|
+
when MINUS
|
276
|
+
raise MINUS + line.strip
|
277
|
+
when PLUS
|
278
|
+
line.strip
|
279
|
+
when COLON
|
280
|
+
line.to_i
|
281
|
+
when DOLLAR
|
282
|
+
bulklen = line.to_i
|
283
|
+
return nil if bulklen == -1
|
284
|
+
data = @sock.read(bulklen)
|
285
|
+
@sock.read(2) # CRLF
|
286
|
+
data
|
287
|
+
when ASTERISK
|
288
|
+
objects = line.to_i
|
289
|
+
return nil if bulklen == -1
|
290
|
+
res = []
|
291
|
+
objects.times {
|
292
|
+
res << read_reply
|
293
|
+
}
|
294
|
+
res
|
477
295
|
else
|
478
|
-
raise
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
def integer_reply
|
483
|
-
Integer(read_proto)
|
484
|
-
end
|
485
|
-
|
486
|
-
def single_line
|
487
|
-
buff = ""
|
488
|
-
while buff[-2..-1] != "\r\n"
|
489
|
-
buff << read(1)
|
490
|
-
end
|
491
|
-
puts "single_line value is #{buff[0..-3].inspect}" if $debug
|
492
|
-
buff[0..-3]
|
493
|
-
end
|
494
|
-
|
495
|
-
def read_socket
|
496
|
-
begin
|
497
|
-
socket = @server.socket
|
498
|
-
while res = socket.read(8096)
|
499
|
-
break if res.size != 8096
|
500
|
-
end
|
501
|
-
#Timeout or server down
|
502
|
-
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED => e
|
503
|
-
server.close
|
504
|
-
puts "Client (#{server.inspect}) disconnected from server: #{e.inspect}\n" if $debug
|
505
|
-
retry
|
506
|
-
rescue Timeout::Error => e
|
507
|
-
#BTM - Ignore this error so we don't go into an endless loop
|
508
|
-
puts "Client (#{server.inspect}) Timeout\n" if $debug
|
509
|
-
#Server down
|
510
|
-
rescue NoMethodError => e
|
511
|
-
puts "Client (#{server.inspect}) tryin server that is down: #{e.inspect}\n Dying!" if $debug
|
512
|
-
raise Errno::ECONNREFUSED
|
513
|
-
#exit
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
def read_proto
|
518
|
-
with_socket_management(@server) do |socket|
|
519
|
-
if res = socket.gets
|
520
|
-
x = res.chomp
|
521
|
-
puts "read_proto is #{x.inspect}\n\n" if $debug
|
522
|
-
x.to_i
|
523
|
-
end
|
296
|
+
raise "Protocol error, got '#{rtype}' as initial reply byte"
|
524
297
|
end
|
525
298
|
end
|
526
|
-
|
527
299
|
end
|