redis_migrator 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/README.md +13 -1
- data/Rakefile +6 -0
- data/lib/redis_migrator/redis_helper.rb +5 -36
- data/lib/redis_migrator/redis_native_migrator.rb +27 -0
- data/lib/redis_migrator/redis_pipe_migrator.rb +66 -0
- data/lib/redis_migrator/redis_populator.rb +1 -1
- data/lib/redis_migrator.rb +19 -20
- data/migrator_benchmark.rb +1 -2
- data/redis_migrator.gemspec +8 -4
- data/spec/different_redis_type_migrator.rb +67 -0
- data/spec/pretested_migrator.rb +47 -0
- data/spec/redis_migrator_spec.rb +41 -0
- data/spec/redis_native_migrator_spec.rb +44 -0
- data/spec/redis_pipe_migrator_spec.rb +51 -0
- data/spec/shared_hosts_context.rb +10 -0
- data/spec/spec_helper.rb +9 -7
- metadata +85 -49
- data/spec/migrator_spec.rb +0 -63
- data/spec/mock_redis/lib/mock_redis/assertions.rb +0 -13
- data/spec/mock_redis/lib/mock_redis/database.rb +0 -432
- data/spec/mock_redis/lib/mock_redis/distributed.rb +0 -6
- data/spec/mock_redis/lib/mock_redis/exceptions.rb +0 -3
- data/spec/mock_redis/lib/mock_redis/expire_wrapper.rb +0 -25
- data/spec/mock_redis/lib/mock_redis/hash_methods.rb +0 -118
- data/spec/mock_redis/lib/mock_redis/list_methods.rb +0 -187
- data/spec/mock_redis/lib/mock_redis/multi_db_wrapper.rb +0 -86
- data/spec/mock_redis/lib/mock_redis/set_methods.rb +0 -126
- data/spec/mock_redis/lib/mock_redis/string_methods.rb +0 -203
- data/spec/mock_redis/lib/mock_redis/transaction_wrapper.rb +0 -80
- data/spec/mock_redis/lib/mock_redis/undef_redis_methods.rb +0 -11
- data/spec/mock_redis/lib/mock_redis/utility_methods.rb +0 -25
- data/spec/mock_redis/lib/mock_redis/version.rb +0 -3
- data/spec/mock_redis/lib/mock_redis/zset.rb +0 -110
- data/spec/mock_redis/lib/mock_redis/zset_methods.rb +0 -210
- data/spec/mock_redis/lib/mock_redis.rb +0 -119
- data/spec/redis_helper_spec.rb +0 -58
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 98a618ae2641c4bf6eadc6cae753d8219f0ab039
|
4
|
+
data.tar.gz: e07f091488059a7d20f7ce0bdf05fb43566632ef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7cda9a9894f2a79c4a500eb15cf9ab6f5efe6fcf512112b9dcfe818c0f672ee4e1894d2aae5d03624db69aee3452821cabf1597c3c6f1588f93785bdd6b5711f
|
7
|
+
data.tar.gz: f6d7e25a19eff8d52cc6704d528eeb6c5fa03bfd5ea4d8f6ac5fec92ba675b14c55d1c3ff8b5d332511b6c69ef86f784e5e5a6d2a0fc381ca9026a1a24126bd5
|
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -22,7 +22,7 @@ and determines for which keys routes were changed. Then it moves those keys to n
|
|
22
22
|
old_redis_hosts = ["redis://host1.com:6379", "redis://host2.com:6379"]
|
23
23
|
|
24
24
|
# a list of redis-urls for a new cluster
|
25
|
-
|
25
|
+
new_redis_hosts = ["redis://host1.com:6379", "redis://host2.com:6379", "redis://host3.com:6379"]
|
26
26
|
|
27
27
|
migrator = Redis::Migrator.new(old_redis_hosts, new_redis_hosts)
|
28
28
|
migrator.run
|
@@ -31,5 +31,17 @@ and determines for which keys routes were changed. Then it moves those keys to n
|
|
31
31
|
* ruby 1.9 or jruby (with --1.9 flag)
|
32
32
|
* redis >=2.4.14 (only on machine where migrator will be running)
|
33
33
|
|
34
|
+
##Contributing
|
35
|
+
|
36
|
+
# First fork the project.
|
37
|
+
# Then bundle
|
38
|
+
bundle
|
39
|
+
|
40
|
+
# and make sure tests pass
|
41
|
+
bundle exec rspec spec
|
42
|
+
|
43
|
+
# Add features and profit.
|
44
|
+
# Send a pull request back to the original repository.
|
45
|
+
|
34
46
|
##TODO
|
35
47
|
* Error handling
|
data/Rakefile
ADDED
@@ -1,9 +1,8 @@
|
|
1
1
|
class Redis
|
2
2
|
module Helper
|
3
|
-
|
4
3
|
def to_redis_proto(*cmd)
|
5
4
|
cmd.inject("*#{cmd.length}\r\n") {|acc, arg|
|
6
|
-
acc << "$#{arg.
|
5
|
+
acc << "$#{arg.to_s.bytesize}\r\n#{arg}\r\n"
|
7
6
|
}
|
8
7
|
end
|
9
8
|
|
@@ -13,40 +12,10 @@ class Redis
|
|
13
12
|
db = path[1..-1].to_i rescue 0
|
14
13
|
|
15
14
|
{
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
15
|
+
host: node.host,
|
16
|
+
port: node.port || 6379,
|
17
|
+
db: db
|
19
18
|
}
|
20
19
|
end
|
21
|
-
|
22
|
-
def copy_string(pipe, key)
|
23
|
-
value = redis.get(key)
|
24
|
-
pipe << to_redis_proto("SET", key, value)
|
25
|
-
end
|
26
|
-
|
27
|
-
def copy_hash(pipe, key)
|
28
|
-
redis.hgetall(key).each do |field, value|
|
29
|
-
pipe << to_redis_proto("HSET", key, field, value)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def copy_list(pipe, key)
|
34
|
-
redis.lrange(key, 0, -1).each do |value|
|
35
|
-
pipe << to_redis_proto("LPUSH", key, value)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def copy_set(pipe, key)
|
40
|
-
redis.smembers(key).each do |member|
|
41
|
-
pipe << to_redis_proto("SADD", key, member)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def copy_zset(pipe, key)
|
46
|
-
redis.zrange(key, 0, -1, :with_scores => true).each_slice(2) do |member, score|
|
47
|
-
pipe << to_redis_proto("ZADD", key, score, member)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
20
|
end
|
52
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Redis
|
2
|
+
class NativeMigrator
|
3
|
+
def initialize(old_hosts)
|
4
|
+
Thread.current[:redis] = Redis::Distributed.new(old_hosts)
|
5
|
+
end
|
6
|
+
|
7
|
+
def redis
|
8
|
+
Thread.current[:redis]
|
9
|
+
end
|
10
|
+
|
11
|
+
def migrate(node_options, keys, _)
|
12
|
+
new_node_options = { host: node_options[:host],
|
13
|
+
port: node_options[:port],
|
14
|
+
db: node_options[:db] }
|
15
|
+
|
16
|
+
grouped_by_old_nodes = keys.group_by do |key|
|
17
|
+
redis.node_for(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
grouped_by_old_nodes.each do |old_node, node_keys|
|
21
|
+
old_node.pipelined do
|
22
|
+
node_keys.each { |key| old_node.migrate(key, new_node_options) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Redis
|
2
|
+
class PipeMigrator
|
3
|
+
include Redis::Helper
|
4
|
+
|
5
|
+
def initialize(old_hosts)
|
6
|
+
Thread.current[:redis] = Redis::Distributed.new(old_hosts)
|
7
|
+
end
|
8
|
+
|
9
|
+
def redis
|
10
|
+
Thread.current[:redis]
|
11
|
+
end
|
12
|
+
|
13
|
+
def migrate(node_options, keys, options)
|
14
|
+
host, port, db = node_options[:host], node_options[:port], node_options[:db]
|
15
|
+
pipe = IO.popen("redis-cli -h #{host} -p #{port} -n #{db} --pipe", IO::RDWR)
|
16
|
+
|
17
|
+
keys.each {|key|
|
18
|
+
copy_key(pipe, key)
|
19
|
+
|
20
|
+
#remove key from old node
|
21
|
+
redis.node_for(key).del(key) unless options[:do_not_remove]
|
22
|
+
}
|
23
|
+
|
24
|
+
pipe.close
|
25
|
+
end
|
26
|
+
|
27
|
+
# Copy a given Redis key to a Redis pipe
|
28
|
+
# @param pipe [IO] a pipe opened redis-cli --pipe
|
29
|
+
# @param key [String] a Redis key that needs to be copied
|
30
|
+
def copy_key(pipe, key)
|
31
|
+
key_type = redis.type(key)
|
32
|
+
return false unless ['list', 'hash', 'string', 'set', 'zset'].include?(key_type)
|
33
|
+
|
34
|
+
self.send("copy_#{key_type}", pipe, key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def copy_string(pipe, key)
|
38
|
+
value = redis.get(key)
|
39
|
+
pipe << to_redis_proto('SET', key, value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def copy_hash(pipe, key)
|
43
|
+
redis.hgetall(key).each do |field, value|
|
44
|
+
pipe << to_redis_proto('HSET', key, field, value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def copy_list(pipe, key)
|
49
|
+
redis.lrange(key, 0, -1).each do |value|
|
50
|
+
pipe << to_redis_proto('RPUSH', key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def copy_set(pipe, key)
|
55
|
+
redis.smembers(key).reverse.each do |member|
|
56
|
+
pipe << to_redis_proto('SADD', key, member)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def copy_zset(pipe, key)
|
61
|
+
redis.zrange(key, 0, -1, with_scores: true).each do |member, score|
|
62
|
+
pipe << to_redis_proto('ZADD', key, score, member)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/redis_migrator.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'redis'
|
3
2
|
require 'redis/distributed'
|
4
3
|
require_relative 'redis_migrator/redis_helper'
|
4
|
+
require_relative 'redis_migrator/redis_pipe_migrator'
|
5
|
+
require_relative 'redis_migrator/redis_native_migrator'
|
5
6
|
|
6
7
|
class Redis
|
7
8
|
class Migrator
|
@@ -48,19 +49,8 @@ class Redis
|
|
48
49
|
# @param options [Hash] additional options, such as :do_not_remove => true
|
49
50
|
def migrate_keys(node, keys, options={})
|
50
51
|
return false if keys.empty? || keys.nil?
|
51
|
-
|
52
|
-
Thread.current[:redis] = Redis::Distributed.new(old_hosts)
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
keys.each {|key|
|
57
|
-
copy_key(pipe, key)
|
58
|
-
|
59
|
-
#remove key from old node
|
60
|
-
redis.node_for(key).del(key) unless options[:do_not_remove]
|
61
|
-
}
|
62
|
-
|
63
|
-
pipe.close
|
53
|
+
migrator(options[:do_not_remove]).new(old_hosts).migrate(node, keys, options)
|
64
54
|
end
|
65
55
|
|
66
56
|
# Runs a migration process for a Redis cluster.
|
@@ -82,15 +72,24 @@ class Redis
|
|
82
72
|
threads.each{|t| t.join}
|
83
73
|
end
|
84
74
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
return false unless ['list', 'hash', 'string', 'set', 'zset'].include?(key_type)
|
75
|
+
private
|
76
|
+
|
77
|
+
def nodes
|
78
|
+
old_cluster.nodes + new_cluster.nodes
|
79
|
+
end
|
91
80
|
|
92
|
-
|
81
|
+
def old_nodes
|
82
|
+
@old_nodes ||= nodes.select { |node| node.info['redis_version'].to_f < 2.6 }
|
93
83
|
end
|
94
84
|
|
85
|
+
def migrator(keep_original)
|
86
|
+
@migrator ||= begin
|
87
|
+
if old_nodes.any? || keep_original
|
88
|
+
Redis::PipeMigrator
|
89
|
+
else
|
90
|
+
Redis::NativeMigrator
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
95
94
|
end # class Migrator
|
96
95
|
end # class Redis
|
data/migrator_benchmark.rb
CHANGED
data/redis_migrator.gemspec
CHANGED
@@ -3,8 +3,8 @@ $:.push File.expand_path("./lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "redis_migrator"
|
6
|
-
s.version = "0.
|
7
|
-
s.date = "
|
6
|
+
s.version = "0.1.1"
|
7
|
+
s.date = "2014-04-10"
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Artem Yankov"]
|
10
10
|
s.email = ["artem.yankov@gmail.com"]
|
@@ -16,5 +16,9 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
|
-
s.add_dependency('redis', '>=
|
20
|
-
|
19
|
+
s.add_dependency('redis', '>= 3.0.0')
|
20
|
+
s.add_development_dependency 'rspec', '~> 2.6'
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'debugger'
|
23
|
+
s.add_development_dependency 'mock_redis'
|
24
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
shared_examples 'different redis type migrator' do
|
2
|
+
context 'key of type' do
|
3
|
+
let(:keys) { [key] }
|
4
|
+
|
5
|
+
subject { migrator.migrate(node, keys, {}) }
|
6
|
+
|
7
|
+
context 'string' do
|
8
|
+
let(:key) { 'a' }
|
9
|
+
|
10
|
+
it 'should copy' do
|
11
|
+
old_cluster.set(key, 'some_string')
|
12
|
+
subject
|
13
|
+
destination_cluster.get(key).should == 'some_string'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
context 'hash' do
|
19
|
+
let(:key) { 'myhash' }
|
20
|
+
|
21
|
+
it 'should copy' do
|
22
|
+
old_cluster.hmset(key,
|
23
|
+
'first_name', 'James',
|
24
|
+
'last_name', 'Randi',
|
25
|
+
'age', '83')
|
26
|
+
subject
|
27
|
+
destination_cluster.hgetall(key).should == {'first_name' => 'James',
|
28
|
+
'last_name' => 'Randi',
|
29
|
+
'age' => '83'}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'list' do
|
34
|
+
let(:key) { 'mylist' }
|
35
|
+
|
36
|
+
it 'should copy' do
|
37
|
+
('a'..'z').to_a.each { |val| old_cluster.lpush(key, val) }
|
38
|
+
values = old_cluster.lrange(key, 0, -1)
|
39
|
+
subject
|
40
|
+
destination_cluster.lrange(key, 0, -1).should == values
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'set' do
|
45
|
+
let(:key) { 'myset' }
|
46
|
+
it 'should copy' do
|
47
|
+
('a'..'z').to_a.each { |val| old_cluster.sadd(key, val) }
|
48
|
+
values = old_cluster.smembers(key)
|
49
|
+
subject
|
50
|
+
destination_cluster.smembers(key).should == values
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'zset' do
|
55
|
+
let(:key) { 'myzset' }
|
56
|
+
it 'should copy' do
|
57
|
+
('a'..'z').to_a.each { |val| old_cluster.zadd(key, rand(100), val) }
|
58
|
+
old_range = old_cluster.zrange(key, 0, -1, with_scores: true).sort
|
59
|
+
|
60
|
+
subject
|
61
|
+
|
62
|
+
new_range = destination_cluster.zrange(key, 0, -1, with_scores: true).sort
|
63
|
+
new_range.should == old_range
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
shared_context 'common keys' do
|
2
|
+
let(:keys) { %w(q s j) }
|
3
|
+
let(:node) { { host: 'localhost', port: 6378, db: 1 } }
|
4
|
+
let(:options) { {} }
|
5
|
+
|
6
|
+
before do
|
7
|
+
prefill_cluster(old_cluster)
|
8
|
+
migrator.migrate(node, keys, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def common_keys(cluster)
|
12
|
+
(cluster.keys('*') & keys).sort
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { common_keys(cluster) }
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples 'pretested migrator' do
|
19
|
+
include_context 'common keys'
|
20
|
+
|
21
|
+
context do
|
22
|
+
let(:cluster) { destination_cluster }
|
23
|
+
it 'should copy given keys to a new cluster' do
|
24
|
+
should == %w(j q s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
shared_examples 'safe pretested migrator' do
|
30
|
+
include_context 'common keys'
|
31
|
+
|
32
|
+
context do
|
33
|
+
let(:cluster) { old_cluster }
|
34
|
+
|
35
|
+
it 'should remove copied keys from the old redis node' do
|
36
|
+
should == []
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when asked to not remove' do
|
40
|
+
let(:options) { { do_not_remove: true } }
|
41
|
+
it 'should keep keys on old node' do
|
42
|
+
should == ["j", "q", "s"]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'shared_hosts_context'
|
2
|
+
|
3
|
+
describe Redis::Migrator do
|
4
|
+
include_context 'shared hosts context'
|
5
|
+
let(:migrator) { Redis::Migrator.new(old_hosts, new_hosts) }
|
6
|
+
|
7
|
+
describe '#changed_keys' do
|
8
|
+
before { prefill_cluster(migrator.old_cluster) }
|
9
|
+
|
10
|
+
it 'should show keys which need migration' do
|
11
|
+
migrator.changed_keys.should == {'redis://localhost:6377/0' => %w(h q s y j m n o)}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#migrator' do
|
16
|
+
subject { migrator.send(:migrator, keep_original) }
|
17
|
+
let(:keep_original) { false }
|
18
|
+
|
19
|
+
before do
|
20
|
+
allow_any_instance_of(MockRedis).to(
|
21
|
+
receive(:info).and_return({ 'redis_version' => version })
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when all instances are old' do
|
26
|
+
let(:version) { '2.4.1' }
|
27
|
+
it { should == Redis::PipeMigrator}
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when all instances are new' do
|
31
|
+
let(:version) { '2.6.14' }
|
32
|
+
it { should == Redis::NativeMigrator }
|
33
|
+
|
34
|
+
context 'when asking to preserve data on source' do
|
35
|
+
let(:keep_original) { true }
|
36
|
+
|
37
|
+
it { should == Redis::PipeMigrator }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'shared_hosts_context'
|
2
|
+
require_relative 'different_redis_type_migrator'
|
3
|
+
require_relative 'pretested_migrator'
|
4
|
+
|
5
|
+
describe Redis::NativeMigrator do
|
6
|
+
let(:migrator) { Redis::NativeMigrator.new(old_hosts) }
|
7
|
+
include_context 'shared hosts context'
|
8
|
+
|
9
|
+
let(:old_cluster) { Redis::Distributed.new(old_hosts) }
|
10
|
+
let(:new_cluster) { Redis::Distributed.new(new_hosts) }
|
11
|
+
|
12
|
+
before { allow(migrator).to receive(:redis).and_return(old_cluster) }
|
13
|
+
|
14
|
+
describe '#migrate' do
|
15
|
+
context do
|
16
|
+
let(:node) { new_cluster.node_for(key) }
|
17
|
+
let(:source_node) { old_cluster.node_for(key) }
|
18
|
+
let(:destination_cluster) { source_node.client.select(10); source_node }
|
19
|
+
before do
|
20
|
+
allow_any_instance_of(MockRedis).to receive(:migrate) do |key|
|
21
|
+
source_node.move(key, 10)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it_behaves_like 'different redis type migrator'
|
26
|
+
end
|
27
|
+
|
28
|
+
context do
|
29
|
+
let(:old_cluster) { Redis::Distributed.new([old_hosts.first]) }
|
30
|
+
let(:source_node) { old_cluster.nodes.first }
|
31
|
+
let(:destination_cluster) { source_node.client.select(10); source_node }
|
32
|
+
|
33
|
+
before do
|
34
|
+
allow_any_instance_of(MockRedis).to receive(:migrate) do |key|
|
35
|
+
source_node.move(key, 10)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it_behaves_like 'pretested migrator'
|
40
|
+
# Not supported in Redis < 3.0
|
41
|
+
# it_behaves_like 'safe pretested migrator'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative 'shared_hosts_context'
|
2
|
+
require_relative 'different_redis_type_migrator'
|
3
|
+
require_relative 'pretested_migrator'
|
4
|
+
|
5
|
+
describe Redis::PipeMigrator do
|
6
|
+
let(:migrator) { Redis::PipeMigrator.new(old_hosts) }
|
7
|
+
include_context 'shared hosts context'
|
8
|
+
|
9
|
+
let(:old_cluster) { Redis::Distributed.new(old_hosts) }
|
10
|
+
let(:destination_cluster) { Redis::Distributed.new(new_hosts) }
|
11
|
+
let(:pipe) { PipeMock.new(destination_cluster) }
|
12
|
+
|
13
|
+
before { allow(migrator).to receive(:redis).and_return(old_cluster) }
|
14
|
+
|
15
|
+
describe '#migrate' do
|
16
|
+
context do
|
17
|
+
let(:node) { {} }
|
18
|
+
before { expect(IO).to receive(:popen).and_return(pipe) }
|
19
|
+
it_behaves_like 'different redis type migrator'
|
20
|
+
end
|
21
|
+
|
22
|
+
context do
|
23
|
+
before do
|
24
|
+
command = 'redis-cli -h localhost -p 6378 -n 1 --pipe'
|
25
|
+
expect(IO).to receive(:popen).with(command, IO::RDWR).and_return(pipe)
|
26
|
+
end
|
27
|
+
|
28
|
+
it_behaves_like 'pretested migrator'
|
29
|
+
it_behaves_like 'safe pretested migrator'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#copy_key' do
|
34
|
+
subject { migrator.copy_key(nil, key) }
|
35
|
+
|
36
|
+
context 'with unknown key' do
|
37
|
+
let(:key) { 'some_key' }
|
38
|
+
it { should == false }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when known set key' do
|
42
|
+
let(:key) { 'a' }
|
43
|
+
before { old_cluster.sadd('a', 1) }
|
44
|
+
|
45
|
+
it 'calls copy_set' do
|
46
|
+
expect(migrator).to receive(:copy_set).with(nil, 'a')
|
47
|
+
subject
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
shared_context 'shared hosts context' do
|
2
|
+
let(:old_hosts) { %w(redis://localhost:6379 redis://localhost:6378) }
|
3
|
+
let(:new_hosts) { old_hosts + ['redis://localhost:6377'] }
|
4
|
+
|
5
|
+
before do
|
6
|
+
expect(Redis).to receive(:new).at_least(1).times do |options|
|
7
|
+
MockRedis.new(options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'rspec'
|
3
2
|
require_relative "../lib/redis_migrator.rb"
|
4
|
-
|
5
|
-
# include patched version of mock_redis
|
6
|
-
# that works with Redis::Distributed
|
7
|
-
require "mock_redis.rb"
|
3
|
+
require 'mock_redis'
|
8
4
|
|
9
5
|
class PipeMock
|
10
6
|
def initialize(redis)
|
@@ -26,4 +22,10 @@ class Redis
|
|
26
22
|
cmd
|
27
23
|
end
|
28
24
|
end
|
29
|
-
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def prefill_cluster(cluster)
|
28
|
+
('a'..'z').to_a.each do |key|
|
29
|
+
(1..5).to_a.each {|val| cluster.sadd(key, val)}
|
30
|
+
end
|
31
|
+
end
|