redis 3.0.0.rc2 → 3.0.0
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/.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
|
|