redis_migrator 0.0.1 → 0.1.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.
- 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
|