redi 0.0.5 → 0.0.7
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/.rvmrc +1 -0
- data/ChangeLog.md +9 -0
- data/Gemfile.lock +3 -1
- data/README.md +44 -12
- data/Rakefile +4 -0
- data/lib/redi.rb +22 -176
- data/lib/redi/mock.rb +47 -0
- data/lib/redi/pool.rb +67 -0
- data/lib/redi/version.rb +2 -2
- data/redi.gemspec +4 -4
- data/test/test_redi.rb +78 -0
- metadata +58 -65
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create 1.9.3-p0@redi
|
data/ChangeLog.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
redi (0.0.
|
4
|
+
redi (0.0.6)
|
5
5
|
redis
|
6
6
|
redis-namespace
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: http://rubygems.org/
|
10
10
|
specs:
|
11
|
+
rake (0.9.2.2)
|
11
12
|
redis (2.2.2)
|
12
13
|
redis-namespace (1.1.0)
|
13
14
|
redis (< 3.0.0)
|
@@ -16,4 +17,5 @@ PLATFORMS
|
|
16
17
|
ruby
|
17
18
|
|
18
19
|
DEPENDENCIES
|
20
|
+
rake
|
19
21
|
redi!
|
data/README.md
CHANGED
@@ -1,14 +1,21 @@
|
|
1
1
|
Redi
|
2
2
|
----------
|
3
3
|
|
4
|
-
Pooled redis
|
5
|
-
|
4
|
+
Pooled redis, add a layer of indirection between server pool and key ring
|
6
5
|
|
7
6
|
The idea comes from http://blog.zawodny.com/2011/02/26/redis-sharding-at-craigslist/
|
8
7
|
|
8
|
+
- - -
|
9
|
+
Install
|
10
|
+
----------
|
9
11
|
gem install redi
|
10
12
|
|
11
|
-
|
13
|
+
Configure
|
14
|
+
----------
|
15
|
+
The configuration should look like a normal redis configuration with the addition of a buckets key.
|
16
|
+
This tells redi how many buckets it should hash keys to before mapping them to the configured server.
|
17
|
+
|
18
|
+
redi.yml:
|
12
19
|
|
13
20
|
development:
|
14
21
|
- :host: 192.168.0.10
|
@@ -20,12 +27,37 @@ Create a configuration file:
|
|
20
27
|
:db: 0
|
21
28
|
:buckets: 65 - 127
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
In the example above, it is possible to scale the 2 configured servers up to 128 servers without
|
31
|
+
re-keying.
|
32
|
+
- - -
|
33
|
+
|
34
|
+
Adding a new server can be done as follows:
|
35
|
+
|
36
|
+
* start a new server process, call it r3.
|
37
|
+
* identify buckets to move to r3 from existing server r2.
|
38
|
+
* setup r3 as slave to replicate from r2.
|
39
|
+
* update configuration to point buckets to r3
|
40
|
+
|
41
|
+
<pre>
|
42
|
+
production:
|
43
|
+
- :host: 192.168.0.10 # r1
|
44
|
+
:port: 6379
|
45
|
+
:db: 0
|
46
|
+
:buckets: 0 - 64
|
47
|
+
- :host: 192.168.0.11 # r2
|
48
|
+
:port: 6380
|
49
|
+
:db: 0
|
50
|
+
:buckets: 65 - 95
|
51
|
+
- :host: 192.168.0.11 # r3
|
52
|
+
:port: 6380
|
53
|
+
:db: 0
|
54
|
+
:buckets: 96 - 127
|
55
|
+
</pre>
|
56
|
+
|
57
|
+
* use bucket key prefixes to prune old keys from r2.
|
58
|
+
|
59
|
+
How you do this can vary depending on your application, but something like the pseudo code below is the idea:
|
60
|
+
|
61
|
+
96..127.times do|i| # NOTE: using redis here not redi as we want to talk to r2 explicitly
|
62
|
+
redis.del(redis.keys("n#{i}*"))
|
63
|
+
end
|
data/Rakefile
ADDED
data/lib/redi.rb
CHANGED
@@ -1,33 +1,37 @@
|
|
1
|
-
require '
|
2
|
-
require 'zlib'
|
3
|
-
require 'yaml'
|
4
|
-
require 'redis'
|
5
|
-
require 'redis/hash_ring'
|
6
|
-
require 'redis/namespace'
|
1
|
+
require 'redi/pool'
|
7
2
|
|
8
3
|
class Redi
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
### these commands are complicated to distribute across a pool and so
|
6
|
+
### for now are unimplemented. Translation: we are lazy.
|
7
|
+
UNIMPLEMENTED_COMMANDS = %w[
|
8
|
+
keys move object randomkey rename renamenx eval
|
9
|
+
mget mset msetnx
|
10
|
+
brpoplpush rpoplpush
|
11
|
+
sdiff sdiffstore sinter sinterstore smove sunion sunionstore
|
12
|
+
zinterstore zunionstore
|
13
|
+
psubscribe publish punsubscribe subscribe unsubscribe
|
14
|
+
discard exec multi unwatch watch
|
15
|
+
auth echo ping quit select
|
16
|
+
bgrewriteaof bgsave config dbsize debug flushdb info lastsave monitor save shutdown slaveof slowlog sync
|
17
|
+
]
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
### raise exceptions on unimplemented/unknown commands, delegate
|
20
|
+
### everything else down to the actual Redis connections
|
21
|
+
def self.method_missing( cmd, *args )
|
22
|
+
if UNIMPLEMENTED_COMMANDS.include?( cmd.to_s )
|
23
|
+
raise NotImplementedError, "#{cmd} has not yet been implemented. Patches welcome!"
|
24
|
+
end
|
17
25
|
|
18
|
-
|
19
|
-
pool.redis_by_key(key).del(key)
|
26
|
+
pool.redis_by_key( args.first ).send( cmd, *args )
|
20
27
|
end
|
21
28
|
|
22
29
|
def self.flushall
|
23
30
|
pool.flushall
|
24
31
|
end
|
25
32
|
|
26
|
-
def self.mock!
|
27
|
-
pool(true).mock!
|
28
|
-
end
|
29
|
-
|
30
33
|
def self.pool(mock=false)
|
34
|
+
require 'redi/mock' if mock
|
31
35
|
@pool ||= Pool.new(self.config,mock)
|
32
36
|
end
|
33
37
|
|
@@ -39,162 +43,4 @@ class Redi
|
|
39
43
|
@config
|
40
44
|
end
|
41
45
|
|
42
|
-
# provide a key to name to host:port mapping
|
43
|
-
#
|
44
|
-
# should have a larger keyspace than servers, this allows scaling up the servers without changing the keyspace mapping
|
45
|
-
#
|
46
|
-
# sample configuration:
|
47
|
-
#
|
48
|
-
# - :host:
|
49
|
-
# :port:
|
50
|
-
# :db:
|
51
|
-
# :buckets: 0 - 64
|
52
|
-
# - :host:
|
53
|
-
# :port:
|
54
|
-
# :db:
|
55
|
-
# :buckets: 65 - 127
|
56
|
-
#
|
57
|
-
class Pool
|
58
|
-
attr_reader :keyspace, :servers
|
59
|
-
def initialize(config,mock=false)
|
60
|
-
key_type = Struct.new(:id, :to_s)
|
61
|
-
|
62
|
-
# build server pool
|
63
|
-
@bucket2server = {}
|
64
|
-
buckets = []
|
65
|
-
@servers = config.map {|cfg|
|
66
|
-
bucket_range = cfg.delete(:buckets)
|
67
|
-
s, e = bucket_range.split('-').map {|n| n.to_i }
|
68
|
-
if mock
|
69
|
-
conn = Mock.new
|
70
|
-
else
|
71
|
-
conn = Redis.new(cfg)
|
72
|
-
end
|
73
|
-
(s..e).each do|i|
|
74
|
-
bucket_name = "n#{i}"
|
75
|
-
buckets << key_type.new(i, bucket_name)
|
76
|
-
@bucket2server[bucket_name] = conn
|
77
|
-
end
|
78
|
-
conn
|
79
|
-
}
|
80
|
-
|
81
|
-
# create the keyring to map redis keys to buckets
|
82
|
-
@keyring = Redis::HashRing.new(buckets)
|
83
|
-
end
|
84
|
-
|
85
|
-
def qualified_key_for(key)
|
86
|
-
bucket = @keyring.get_node(key)
|
87
|
-
"#{bucket.to_s}:#{key}"
|
88
|
-
end
|
89
|
-
|
90
|
-
def redis_by_key(key)
|
91
|
-
bucket = @keyring.get_node(key)
|
92
|
-
Redis::Namespace.new(bucket.to_s, :redis => @bucket2server[bucket.to_s])
|
93
|
-
end
|
94
|
-
|
95
|
-
def flushall
|
96
|
-
@servers.map {|s| s.flushall }
|
97
|
-
end
|
98
|
-
|
99
|
-
def mock!
|
100
|
-
@servers.map! {|s| Mock.new }
|
101
|
-
@bucket2server.keys.each_with_index do|k,i|
|
102
|
-
@bucket2server[k] = @servers[i % @servers.size]
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
class Mock
|
109
|
-
def initialize
|
110
|
-
@store = {}
|
111
|
-
end
|
112
|
-
|
113
|
-
def get(key)
|
114
|
-
@store[key]
|
115
|
-
end
|
116
|
-
|
117
|
-
def del(key)
|
118
|
-
@store.delete(key)
|
119
|
-
end
|
120
|
-
|
121
|
-
def set(key, val)
|
122
|
-
@store[key] = val
|
123
|
-
end
|
124
|
-
|
125
|
-
def mget(*keys)
|
126
|
-
keys.map {|k| get(k) }
|
127
|
-
end
|
128
|
-
|
129
|
-
def flushall
|
130
|
-
@store = {}
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
if __FILE__ == $0
|
138
|
-
require 'rubygems'
|
139
|
-
require 'test/unit'
|
140
|
-
|
141
|
-
TEST_CONFIG = %(
|
142
|
-
- :host: 127.0.0.1
|
143
|
-
:port: 6379
|
144
|
-
:db: 0
|
145
|
-
:buckets: 0-64
|
146
|
-
- :host: 127.0.0.1
|
147
|
-
:port: 6379
|
148
|
-
:db: 1
|
149
|
-
:buckets: 65-127
|
150
|
-
)
|
151
|
-
class TestIt < Test::Unit::TestCase
|
152
|
-
|
153
|
-
def test_redis_pool
|
154
|
-
pool = Redi::Pool.new(YAML.load(TEST_CONFIG))
|
155
|
-
redis = pool.redis_by_key('me:foo:1')
|
156
|
-
assert_equal "n25", redis.namespace
|
157
|
-
redis.flushall
|
158
|
-
redis.set("me:foo:1", "hello")
|
159
|
-
assert_equal "hello", redis.get("me:foo:1")
|
160
|
-
end
|
161
|
-
|
162
|
-
def test_using_hash_ring_strategy
|
163
|
-
node_class = Struct.new(:id, :to_s)
|
164
|
-
buckets = []
|
165
|
-
128.times do|i|
|
166
|
-
buckets << node_class.new(i, "n#{i}")
|
167
|
-
end
|
168
|
-
|
169
|
-
servers = []
|
170
|
-
2.times do|i|
|
171
|
-
servers << node_class.new(i, "s#{i}")
|
172
|
-
end
|
173
|
-
|
174
|
-
ring1 = Redis::HashRing.new(buckets)
|
175
|
-
|
176
|
-
bucket2server = {}
|
177
|
-
buckets.each do|bucket|
|
178
|
-
bucket2server[bucket.to_s] = servers[bucket.id % servers.size].to_s
|
179
|
-
end
|
180
|
-
|
181
|
-
puts bucket2server.inspect
|
182
|
-
|
183
|
-
servers_used = {}
|
184
|
-
|
185
|
-
buckets_used = {}
|
186
|
-
6400.times do|i|
|
187
|
-
key = "me:foo#{i}"
|
188
|
-
bucket = ring1.get_node(key)
|
189
|
-
server = bucket2server[bucket.to_s]#ring2.get_node(name.to_s)
|
190
|
-
#puts "#{key} -> #{name} -> #{server}"
|
191
|
-
servers_used[ server.to_s ] ||= 0
|
192
|
-
servers_used[ server.to_s ] += 1
|
193
|
-
buckets_used[ bucket.to_s ] ||= 0
|
194
|
-
buckets_used[ bucket.to_s ] += 1
|
195
|
-
end
|
196
|
-
puts servers_used.inspect
|
197
|
-
puts buckets_used.inspect
|
198
|
-
end
|
199
|
-
end
|
200
46
|
end
|
data/lib/redi/mock.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#
|
2
|
+
# Instead of writing/reading from redis, create a in memory Hash, making it easy to
|
3
|
+
# test without redis server running
|
4
|
+
#
|
5
|
+
class Redi
|
6
|
+
|
7
|
+
def self.mock!
|
8
|
+
pool(true).mock!
|
9
|
+
end
|
10
|
+
|
11
|
+
class Pool
|
12
|
+
|
13
|
+
def mock!
|
14
|
+
@servers.map! {|s| Mock.new }
|
15
|
+
@bucket2server.keys.each_with_index do|k,i|
|
16
|
+
@bucket2server[k] = @servers[i % @servers.size]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mock
|
22
|
+
def initialize
|
23
|
+
@store = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(key)
|
27
|
+
@store[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def del(key)
|
31
|
+
@store.delete(key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def set(key, val)
|
35
|
+
@store[key] = val
|
36
|
+
end
|
37
|
+
|
38
|
+
def mget(*keys)
|
39
|
+
keys.map {|k| get(k) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def flushall
|
43
|
+
@store = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/redi/pool.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'redis'
|
4
|
+
require 'redis/hash_ring'
|
5
|
+
require 'redis/namespace'
|
6
|
+
|
7
|
+
# provide a key to name to host:port mapping
|
8
|
+
#
|
9
|
+
# should have a larger keyspace than servers, this allows scaling up the servers without changing the keyspace mapping
|
10
|
+
#
|
11
|
+
# sample configuration:
|
12
|
+
#
|
13
|
+
# - :host:
|
14
|
+
# :port:
|
15
|
+
# :db:
|
16
|
+
# :buckets: 0 - 64
|
17
|
+
# - :host:
|
18
|
+
# :port:
|
19
|
+
# :db:
|
20
|
+
# :buckets: 65 - 127
|
21
|
+
#
|
22
|
+
class Redi
|
23
|
+
class Pool
|
24
|
+
attr_reader :keyspace, :servers
|
25
|
+
def initialize(config,mock=false)
|
26
|
+
key_type = Struct.new(:id, :to_s)
|
27
|
+
|
28
|
+
# build server pool
|
29
|
+
@bucket2server = {}
|
30
|
+
buckets = []
|
31
|
+
@servers = config.map {|cfg|
|
32
|
+
bucket_range = cfg.delete(:buckets)
|
33
|
+
s, e = bucket_range.split('-').map {|n| n.to_i }
|
34
|
+
if mock
|
35
|
+
require 'redi/mock'
|
36
|
+
conn = Mock.new
|
37
|
+
else
|
38
|
+
conn = Redis.new(cfg)
|
39
|
+
end
|
40
|
+
(s..e).each do|i|
|
41
|
+
bucket_name = "n#{i}"
|
42
|
+
buckets << key_type.new(i, bucket_name)
|
43
|
+
@bucket2server[bucket_name] = conn
|
44
|
+
end
|
45
|
+
conn
|
46
|
+
}
|
47
|
+
|
48
|
+
# create the keyring to map redis keys to buckets
|
49
|
+
@keyring = Redis::HashRing.new(buckets)
|
50
|
+
end
|
51
|
+
|
52
|
+
def qualified_key_for(key)
|
53
|
+
bucket = @keyring.get_node(key)
|
54
|
+
"#{bucket.to_s}:#{key}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def redis_by_key(key)
|
58
|
+
bucket = @keyring.get_node(key)
|
59
|
+
Redis::Namespace.new(bucket.to_s, :redis => @bucket2server[bucket.to_s])
|
60
|
+
end
|
61
|
+
|
62
|
+
def flushall
|
63
|
+
@servers.map {|s| s.flushall }
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/redi/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
class Redi
|
2
|
+
VERSION = "0.0.7"
|
3
3
|
end
|
data/redi.gemspec
CHANGED
@@ -4,9 +4,9 @@ require "redi/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "redi"
|
7
|
-
s.version =
|
8
|
-
s.authors = ["Todd Fisher"]
|
9
|
-
s.email = ["todd.fisher@livingsocial.com"]
|
7
|
+
s.version = Redi::VERSION
|
8
|
+
s.authors = ["Todd Fisher", "Ben Bleything"]
|
9
|
+
s.email = ["todd.fisher@livingsocial.com", "ben@bleything.net"]
|
10
10
|
s.homepage = "http://livingsocial.com/"
|
11
11
|
s.summary = %q{Redi multi redis scaling "to infinity and beyond!"}
|
12
12
|
s.description = %q{hash keys to intermediate buckets allowing you to more easily scale out to more severs later}
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
-
|
22
|
+
s.add_development_dependency "rake"
|
23
23
|
|
24
24
|
s.add_runtime_dependency "redis"
|
25
25
|
s.add_runtime_dependency "redis-namespace"
|
data/test/test_redi.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'redi'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
TEST_CONFIG = %(
|
6
|
+
- :host: 127.0.0.1
|
7
|
+
:port: 6379
|
8
|
+
:db: 6
|
9
|
+
:buckets: 0-64
|
10
|
+
- :host: 127.0.0.1
|
11
|
+
:port: 6379
|
12
|
+
:db: 7
|
13
|
+
:buckets: 65-127
|
14
|
+
)
|
15
|
+
|
16
|
+
class TestRedi < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
Redi.config = YAML.load( TEST_CONFIG )
|
20
|
+
Redi.flushall
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
Redi.flushall
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_unimplemented_commands
|
28
|
+
assert_raises( NotImplementedError, /has not yet been implemented/ ) do
|
29
|
+
Redi.brpoplpush 'source', 'destination', 'timeout'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_redis_pool
|
34
|
+
redis = Redi.pool.redis_by_key('me:foo:1')
|
35
|
+
assert_equal "n25", redis.namespace
|
36
|
+
redis.set("me:foo:1", "hello")
|
37
|
+
assert_equal "hello", redis.get("me:foo:1")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_using_hash_ring_strategy
|
41
|
+
node_class = Struct.new(:id, :to_s)
|
42
|
+
buckets = []
|
43
|
+
128.times do|i|
|
44
|
+
buckets << node_class.new(i, "n#{i}")
|
45
|
+
end
|
46
|
+
|
47
|
+
servers = []
|
48
|
+
2.times do|i|
|
49
|
+
servers << node_class.new(i, "s#{i}")
|
50
|
+
end
|
51
|
+
|
52
|
+
ring1 = Redis::HashRing.new(buckets)
|
53
|
+
|
54
|
+
bucket2server = {}
|
55
|
+
buckets.each do|bucket|
|
56
|
+
bucket2server[bucket.to_s] = servers[bucket.id % servers.size].to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
servers_used = {}
|
60
|
+
|
61
|
+
buckets_used = {}
|
62
|
+
6400.times do|i|
|
63
|
+
key = "me:foo#{i}"
|
64
|
+
bucket = ring1.get_node(key)
|
65
|
+
index = bucket.to_s.gsub(/n/,'').to_i
|
66
|
+
assert index < 128 && index >= 0, "bucket out of range: #{bucket}"
|
67
|
+
server = bucket2server[bucket.to_s]
|
68
|
+
servers_used[ server.to_s ] ||= 0
|
69
|
+
servers_used[ server.to_s ] += 1
|
70
|
+
buckets_used[ bucket.to_s ] ||= 0
|
71
|
+
buckets_used[ bucket.to_s ] += 1
|
72
|
+
end
|
73
|
+
|
74
|
+
assert_equal 3228, servers_used["s0"], "when hashing 6400 times in sequence, s0 should have 3228 hits"
|
75
|
+
assert_equal 3172, servers_used["s1"], "when hashing 6400 times in sequence, s1 should have 3172 hits"
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
metadata
CHANGED
@@ -1,102 +1,95 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: redi
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 5
|
10
|
-
version: 0.0.5
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Todd Fisher
|
9
|
+
- Ben Bleything
|
14
10
|
autorequire:
|
15
11
|
bindir: bin
|
16
12
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
date: 2011-11-02 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: &2151905420 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
23
24
|
prerelease: false
|
24
|
-
|
25
|
+
version_requirements: *2151905420
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: redis
|
28
|
+
requirement: &2151904760 !ruby/object:Gem::Requirement
|
25
29
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
33
34
|
type: :runtime
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: redis-namespace
|
37
35
|
prerelease: false
|
38
|
-
|
36
|
+
version_requirements: *2151904760
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: redis-namespace
|
39
|
+
requirement: &2151904240 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
47
45
|
type: :runtime
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *2151904240
|
48
|
+
description: hash keys to intermediate buckets allowing you to more easily scale out
|
49
|
+
to more severs later
|
50
|
+
email:
|
51
51
|
- todd.fisher@livingsocial.com
|
52
|
+
- ben@bleything.net
|
52
53
|
executables: []
|
53
|
-
|
54
54
|
extensions: []
|
55
|
-
|
56
55
|
extra_rdoc_files: []
|
57
|
-
|
58
|
-
|
56
|
+
files:
|
57
|
+
- .rvmrc
|
59
58
|
- ChangeLog.md
|
60
59
|
- Gemfile
|
61
60
|
- Gemfile.lock
|
62
61
|
- README.md
|
62
|
+
- Rakefile
|
63
63
|
- init.rb
|
64
64
|
- lib/redi.rb
|
65
|
+
- lib/redi/mock.rb
|
66
|
+
- lib/redi/pool.rb
|
65
67
|
- lib/redi/version.rb
|
66
68
|
- redi.gemspec
|
67
|
-
|
69
|
+
- test/test_redi.rb
|
68
70
|
homepage: http://livingsocial.com/
|
69
71
|
licenses: []
|
70
|
-
|
71
72
|
post_install_message:
|
72
73
|
rdoc_options: []
|
73
|
-
|
74
|
-
require_paths:
|
74
|
+
require_paths:
|
75
75
|
- lib
|
76
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
77
|
none: false
|
78
|
-
requirements:
|
79
|
-
- -
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
82
|
-
|
83
|
-
- 0
|
84
|
-
version: "0"
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
83
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
94
88
|
requirements: []
|
95
|
-
|
96
89
|
rubyforge_project: redi
|
97
|
-
rubygems_version: 1.
|
90
|
+
rubygems_version: 1.8.10
|
98
91
|
signing_key:
|
99
92
|
specification_version: 3
|
100
93
|
summary: Redi multi redis scaling "to infinity and beyond!"
|
101
|
-
test_files:
|
102
|
-
|
94
|
+
test_files:
|
95
|
+
- test/test_redi.rb
|