redis 3.0.0.rc2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.order +169 -0
- data/CHANGELOG.md +79 -9
- data/README.md +7 -0
- data/Rakefile +167 -0
- data/lib/redis.rb +1265 -1148
- data/lib/redis/client.rb +11 -18
- data/lib/redis/distributed.rb +337 -280
- data/lib/redis/pipeline.rb +12 -4
- data/lib/redis/version.rb +1 -1
- data/redis.gemspec +1 -1
- data/test/commands_on_lists_test.rb +0 -30
- data/test/commands_on_strings_test.rb +0 -6
- data/test/distributed_scripting_test.rb +102 -0
- data/test/internals_test.rb +33 -35
- data/test/lint/lists.rb +42 -12
- data/test/lint/strings.rb +6 -0
- data/test/lint/value_types.rb +10 -6
- data/test/scripting_test.rb +78 -0
- metadata +12 -8
data/.order
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
{
|
2
|
+
"connection": [
|
3
|
+
"auth",
|
4
|
+
"select",
|
5
|
+
"ping",
|
6
|
+
"echo",
|
7
|
+
"quit"
|
8
|
+
],
|
9
|
+
"server": [
|
10
|
+
"bgrewriteaof",
|
11
|
+
"bgsave",
|
12
|
+
"config",
|
13
|
+
"dbsize",
|
14
|
+
"debug",
|
15
|
+
"flushall",
|
16
|
+
"flushdb",
|
17
|
+
"info",
|
18
|
+
"lastsave",
|
19
|
+
"monitor",
|
20
|
+
"save",
|
21
|
+
"shutdown",
|
22
|
+
"slaveof",
|
23
|
+
"slowlog",
|
24
|
+
"sync",
|
25
|
+
"time"
|
26
|
+
],
|
27
|
+
"generic": [
|
28
|
+
"persist",
|
29
|
+
"expire",
|
30
|
+
"expireat",
|
31
|
+
"ttl",
|
32
|
+
"pexpire",
|
33
|
+
"pexpireat",
|
34
|
+
"pttl",
|
35
|
+
"dump",
|
36
|
+
"restore",
|
37
|
+
"del",
|
38
|
+
"exists",
|
39
|
+
"keys",
|
40
|
+
"migrate",
|
41
|
+
"move",
|
42
|
+
"object",
|
43
|
+
"randomkey",
|
44
|
+
"rename",
|
45
|
+
"renamenx",
|
46
|
+
"sort",
|
47
|
+
"type"
|
48
|
+
],
|
49
|
+
"string": [
|
50
|
+
"decr",
|
51
|
+
"decrby",
|
52
|
+
"incr",
|
53
|
+
"incrby",
|
54
|
+
"incrbyfloat",
|
55
|
+
"set",
|
56
|
+
"setex",
|
57
|
+
"psetex",
|
58
|
+
"setnx",
|
59
|
+
"mset",
|
60
|
+
"mapped_mset",
|
61
|
+
"msetnx",
|
62
|
+
"mapped_msetnx",
|
63
|
+
"get",
|
64
|
+
"mget",
|
65
|
+
"mapped_mget",
|
66
|
+
"setrange",
|
67
|
+
"getrange",
|
68
|
+
"setbit",
|
69
|
+
"getbit",
|
70
|
+
"append",
|
71
|
+
"bitcount",
|
72
|
+
"getset",
|
73
|
+
"strlen"
|
74
|
+
],
|
75
|
+
"list": [
|
76
|
+
"llen",
|
77
|
+
"lpush",
|
78
|
+
"lpushx",
|
79
|
+
"rpush",
|
80
|
+
"rpushx",
|
81
|
+
"lpop",
|
82
|
+
"rpop",
|
83
|
+
"rpoplpush",
|
84
|
+
"_bpop",
|
85
|
+
"blpop",
|
86
|
+
"brpop",
|
87
|
+
"brpoplpush",
|
88
|
+
"lindex",
|
89
|
+
"linsert",
|
90
|
+
"lrange",
|
91
|
+
"lrem",
|
92
|
+
"lset",
|
93
|
+
"ltrim",
|
94
|
+
"bitop"
|
95
|
+
],
|
96
|
+
"set": [
|
97
|
+
"scard",
|
98
|
+
"sadd",
|
99
|
+
"srem",
|
100
|
+
"spop",
|
101
|
+
"srandmember",
|
102
|
+
"smove",
|
103
|
+
"sismember",
|
104
|
+
"smembers",
|
105
|
+
"sdiff",
|
106
|
+
"sdiffstore",
|
107
|
+
"sinter",
|
108
|
+
"sinterstore",
|
109
|
+
"sunion",
|
110
|
+
"sunionstore"
|
111
|
+
],
|
112
|
+
"sorted_set": [
|
113
|
+
"zcard",
|
114
|
+
"zadd",
|
115
|
+
"zincrby",
|
116
|
+
"zrem",
|
117
|
+
"zscore",
|
118
|
+
"zrange",
|
119
|
+
"zrevrange",
|
120
|
+
"zrank",
|
121
|
+
"zrevrank",
|
122
|
+
"zremrangebyrank",
|
123
|
+
"zrangebyscore",
|
124
|
+
"zrevrangebyscore",
|
125
|
+
"zremrangebyscore",
|
126
|
+
"zcount",
|
127
|
+
"zinterstore",
|
128
|
+
"zunionstore"
|
129
|
+
],
|
130
|
+
"hash": [
|
131
|
+
"hlen",
|
132
|
+
"hset",
|
133
|
+
"hsetnx",
|
134
|
+
"hmset",
|
135
|
+
"mapped_hmset",
|
136
|
+
"hget",
|
137
|
+
"hmget",
|
138
|
+
"mapped_hmget",
|
139
|
+
"hdel",
|
140
|
+
"hexists",
|
141
|
+
"hincrby",
|
142
|
+
"hincrbyfloat",
|
143
|
+
"hkeys",
|
144
|
+
"hvals",
|
145
|
+
"hgetall"
|
146
|
+
],
|
147
|
+
"pubsub": [
|
148
|
+
"publish",
|
149
|
+
"subscribed?",
|
150
|
+
"subscribe",
|
151
|
+
"unsubscribe",
|
152
|
+
"psubscribe",
|
153
|
+
"punsubscribe"
|
154
|
+
],
|
155
|
+
"transactions": [
|
156
|
+
"watch",
|
157
|
+
"unwatch",
|
158
|
+
"pipelined",
|
159
|
+
"multi",
|
160
|
+
"exec",
|
161
|
+
"discard"
|
162
|
+
],
|
163
|
+
"scripting": [
|
164
|
+
"script",
|
165
|
+
"_eval",
|
166
|
+
"eval",
|
167
|
+
"evalsha"
|
168
|
+
]
|
169
|
+
}
|
data/CHANGELOG.md
CHANGED
@@ -1,26 +1,89 @@
|
|
1
|
-
# 3.0
|
1
|
+
# 3.0.0
|
2
|
+
|
3
|
+
### Upgrading from 2.x to 3.0
|
4
|
+
|
5
|
+
The following items are the most important changes to review when
|
6
|
+
upgrading from redis-rb 2.x. A full list of changes can be found below.
|
7
|
+
|
8
|
+
* The methods for the following commands have changed the arguments they
|
9
|
+
take, their return value, or both.
|
10
|
+
|
11
|
+
* `BLPOP`, `BRPOP`, `BRPOPLPUSH`
|
12
|
+
* `SORT`
|
13
|
+
* `MSETNX`
|
14
|
+
* `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZREVRANGEBYSCORE`
|
15
|
+
* `ZINCRBY`, `ZSCORE`
|
16
|
+
|
17
|
+
* The return value from `#pipelined` and `#multi` no longer contains
|
18
|
+
unprocessed replies, but the same replies that would be returned if
|
19
|
+
the command had not been executed in these blocks.
|
20
|
+
|
21
|
+
* The client raises custom errors on connection errors, instead of
|
22
|
+
`RuntimeError` and errors in the `Errno` family.
|
23
|
+
|
24
|
+
### Changes
|
25
|
+
|
26
|
+
* Added support for scripting commands (Redis 2.6).
|
27
|
+
|
28
|
+
Scripts can be executed using `#eval` and `#evalsha`. Both can
|
29
|
+
commands can either take two arrays to specify `KEYS` and `ARGV`, or
|
30
|
+
take a hash containing `:keys` and `:argv` to specify `KEYS` and
|
31
|
+
`ARGV`.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
redis.eval("return ARGV[1] * ARGV[2]", :argv => [2, 3])
|
35
|
+
# => 6
|
36
|
+
```
|
37
|
+
|
38
|
+
Subcommands of the `SCRIPT` command can be executed via the
|
39
|
+
`#script` method.
|
40
|
+
|
41
|
+
For example:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
redis.script(:load, "return ARGV[1] * ARGV[2]")
|
45
|
+
# => "58db5d365a1922f32e7aa717722141ea9c2b0cf3"
|
46
|
+
redis.script(:exists, "58db5d365a1922f32e7aa717722141ea9c2b0cf3")
|
47
|
+
# => true
|
48
|
+
redis.script(:flush)
|
49
|
+
# => "OK"
|
50
|
+
```
|
2
51
|
|
3
52
|
* The repository now lives at [https://github.com/redis/redis-rb](https://github.com/redis/redis-rb).
|
4
53
|
Thanks, Ezra!
|
5
54
|
|
6
|
-
* Added support for `PEXPIRE`, `
|
55
|
+
* Added support for `PEXPIRE`, `PEXPIREAT`, `PTTL`, `PSETEX`,
|
7
56
|
`INCRYBYFLOAT`, `HINCRYBYFLOAT` and `TIME` (Redis 2.6).
|
8
57
|
|
9
58
|
* `Redis.current` is now thread unsafe, because the client itself is thread safe.
|
10
59
|
|
11
60
|
In the future you'll be able to do something like:
|
12
61
|
|
13
|
-
|
62
|
+
```ruby
|
63
|
+
Redis.current = Redis::Pool.connect
|
64
|
+
```
|
14
65
|
|
15
66
|
This makes `Redis.current` actually usable in multi-threaded environments,
|
16
67
|
while not affecting those running a single thread.
|
17
68
|
|
18
|
-
* Change API for `BLPOP`, `BRPOP` and `BRPOPLPUSH`.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
69
|
+
* Change API for `BLPOP`, `BRPOP` and `BRPOPLPUSH`.
|
70
|
+
|
71
|
+
Both `BLPOP` and `BRPOP` now take a single argument equal to a
|
72
|
+
string key, or an array with string keys, followed by an optional
|
73
|
+
hash with a `:timeout` key. When not specified, the timeout defaults
|
74
|
+
to `0` to not time out.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
redis.blpop(["list1", "list2"], :timeout => 1.0)
|
78
|
+
```
|
79
|
+
|
80
|
+
`BRPOPLPUSH` also takes an optional hash with a `:timeout` key as
|
81
|
+
last argument for consistency. When not specified, the timeout
|
82
|
+
defaults to `0` to not time out.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
redis.brpoplpush("some_list", "another_list", :timeout => 1.0)
|
86
|
+
```
|
24
87
|
|
25
88
|
* When `SORT` is passed multiple key patterns to get via the `:get`
|
26
89
|
option, it now returns an array per result element, holding all `GET`
|
@@ -32,6 +95,13 @@
|
|
32
95
|
now return an array containing `[String, Float]` pairs when
|
33
96
|
`:with_scores => true` is passed.
|
34
97
|
|
98
|
+
For example:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
redis.zrange("zset", 0, -1, :with_scores => true)
|
102
|
+
# => [["foo", 1.0], ["bar", 2.0]]
|
103
|
+
```
|
104
|
+
|
35
105
|
* The `ZINCRBY` and `ZSCORE` commands now return a `Float` score instead
|
36
106
|
of a string holding a representation of the score.
|
37
107
|
|
data/README.md
CHANGED
@@ -12,6 +12,13 @@ A Ruby client that tries to match Redis' API one-to-one, while still
|
|
12
12
|
providing an idiomatic interface. It features thread-safety, client-side
|
13
13
|
sharding, pipelining, and an obsession for performance.
|
14
14
|
|
15
|
+
## Upgrading from 2.x to 3.0
|
16
|
+
|
17
|
+
Please refer to the [CHANGELOG][changelog-3.0.0] for a summary of the
|
18
|
+
most important changes, as well as a full list of changes.
|
19
|
+
|
20
|
+
[changelog-3.0.0]: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300
|
21
|
+
|
15
22
|
## Getting started
|
16
23
|
|
17
24
|
As of version 2.0 this client only targets Redis version 2.0 and higher.
|
data/Rakefile
CHANGED
@@ -148,6 +148,71 @@ namespace :doc do
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
+
class Source
|
152
|
+
|
153
|
+
MATCHER = "(?:\\s{%d}#[^\\n]*\\n)*^\\s{%d}def ([a-z_?]+)(?:\(.*?\))?\\n.*?^\\s{%d}end\\n\\n"
|
154
|
+
|
155
|
+
def initialize(data, options = {})
|
156
|
+
@doc = parse(File.read(data), options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def methods
|
160
|
+
@doc.select do |d|
|
161
|
+
d.is_a?(Method)
|
162
|
+
end.map do |d|
|
163
|
+
d.name
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def move(a, b)
|
168
|
+
ao = @doc.find { |m| m.is_a?(Method) && m.name == a }
|
169
|
+
bo = @doc.find { |m| m.is_a?(Method) && m.name == b }
|
170
|
+
ai = @doc.index(ao)
|
171
|
+
bi = @doc.index(bo)
|
172
|
+
|
173
|
+
@doc.delete_at(ai)
|
174
|
+
@doc.insert(bi, ao)
|
175
|
+
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_s
|
180
|
+
@doc.join
|
181
|
+
end
|
182
|
+
|
183
|
+
protected
|
184
|
+
|
185
|
+
def parse(data, options = {})
|
186
|
+
re = Regexp.new(MATCHER % ([options[:indent]] * 3), Regexp::MULTILINE)
|
187
|
+
tail = data.dup
|
188
|
+
doc = []
|
189
|
+
|
190
|
+
while match = re.match(tail)
|
191
|
+
doc << match.pre_match
|
192
|
+
doc << Method.new(match)
|
193
|
+
tail = match.post_match
|
194
|
+
end
|
195
|
+
|
196
|
+
doc << tail if tail
|
197
|
+
doc
|
198
|
+
end
|
199
|
+
|
200
|
+
class Method
|
201
|
+
|
202
|
+
def initialize(match)
|
203
|
+
@match = match
|
204
|
+
end
|
205
|
+
|
206
|
+
def name
|
207
|
+
@match[1]
|
208
|
+
end
|
209
|
+
|
210
|
+
def to_s
|
211
|
+
@match[0]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
151
216
|
namespace :commands do
|
152
217
|
def redis_commands
|
153
218
|
$redis_commands ||= doc.keys.map do |key|
|
@@ -164,6 +229,108 @@ namespace :commands do
|
|
164
229
|
end
|
165
230
|
end
|
166
231
|
|
232
|
+
task :order do
|
233
|
+
require "json"
|
234
|
+
|
235
|
+
reference = if File.exist?(".order")
|
236
|
+
JSON.parse(File.read(".order"))
|
237
|
+
else
|
238
|
+
{}
|
239
|
+
end
|
240
|
+
|
241
|
+
buckets = {}
|
242
|
+
doc.each do |k, v|
|
243
|
+
buckets[v["group"]] ||= []
|
244
|
+
buckets[v["group"]] << k.split.first.downcase
|
245
|
+
buckets[v["group"]].uniq!
|
246
|
+
end
|
247
|
+
|
248
|
+
result = (reference.keys + (buckets.keys - reference.keys)).map do |g|
|
249
|
+
[g, reference[g] + (buckets[g] - reference[g])]
|
250
|
+
end
|
251
|
+
|
252
|
+
File.open(".order", "w") do |f|
|
253
|
+
f.write(JSON.pretty_generate(Hash[result]))
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def reorder(file, options = {})
|
258
|
+
require "json"
|
259
|
+
require "set"
|
260
|
+
|
261
|
+
STDERR.puts "reordering #{file}..."
|
262
|
+
|
263
|
+
reference = if File.exist?(".order")
|
264
|
+
JSON.parse(File.read(".order"))
|
265
|
+
else
|
266
|
+
{}
|
267
|
+
end
|
268
|
+
|
269
|
+
dst = Source.new(file, options)
|
270
|
+
|
271
|
+
src_methods = reference.map { |k, v| v }.flatten
|
272
|
+
dst_methods = dst.methods
|
273
|
+
|
274
|
+
src_set = Set.new(src_methods)
|
275
|
+
dst_set = Set.new(dst_methods)
|
276
|
+
|
277
|
+
intersection = src_set & dst_set
|
278
|
+
intersection.delete("initialize")
|
279
|
+
|
280
|
+
loop do
|
281
|
+
src_methods = reference.map { |k, v| v }.flatten
|
282
|
+
dst_methods = dst.methods
|
283
|
+
|
284
|
+
src_methods = src_methods.select do |m|
|
285
|
+
intersection.include?(m)
|
286
|
+
end
|
287
|
+
|
288
|
+
dst_methods = dst_methods.select do |m|
|
289
|
+
intersection.include?(m)
|
290
|
+
end
|
291
|
+
|
292
|
+
if src_methods == dst_methods
|
293
|
+
break
|
294
|
+
end
|
295
|
+
|
296
|
+
rv = yield(src_methods, dst_methods, dst)
|
297
|
+
break if rv == false
|
298
|
+
end
|
299
|
+
|
300
|
+
File.open(file, "w") do |f|
|
301
|
+
f.write(dst.to_s)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
task :reorder do
|
306
|
+
blk = lambda do |src_methods, dst_methods, dst|
|
307
|
+
src_methods.zip(dst_methods).each do |a, b|
|
308
|
+
if a != b
|
309
|
+
dst.move(a, b)
|
310
|
+
break
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
reorder "lib/redis.rb", :indent => 2, &blk
|
316
|
+
reorder "lib/redis/distributed.rb", :indent => 4, &blk
|
317
|
+
end
|
318
|
+
|
319
|
+
def missing(file, options = {})
|
320
|
+
src = Source.new(file, options)
|
321
|
+
|
322
|
+
defined_methods = src.methods.map(&:downcase)
|
323
|
+
required_methods = redis_commands.map(&:downcase)
|
324
|
+
|
325
|
+
STDOUT.puts "missing in #{file}:"
|
326
|
+
STDOUT.puts (required_methods - defined_methods).inspect
|
327
|
+
end
|
328
|
+
|
329
|
+
task :missing do
|
330
|
+
missing "lib/redis.rb", :indent => 2
|
331
|
+
missing "lib/redis/distributed.rb", :indent => 4
|
332
|
+
end
|
333
|
+
|
167
334
|
def document(file)
|
168
335
|
source = File.read(file)
|
169
336
|
|