redis-copy 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +8 -7
- data/lib/redis-copy.rb +22 -0
- data/lib/redis-copy/cli.rb +17 -2
- data/lib/redis-copy/key-emitter.rb +13 -6
- data/lib/redis-copy/strategy.rb +45 -0
- data/lib/redis-copy/strategy/classic.rb +1 -0
- data/lib/redis-copy/strategy/new.rb +2 -0
- data/lib/redis-copy/ui.rb +3 -0
- data/lib/redis-copy/version.rb +1 -1
- data/spec/redis-copy/key-emitter_spec.rb +18 -0
- data/spec/redis-copy/strategy_spec.rb +78 -102
- metadata +2 -2
data/README.md
CHANGED
@@ -21,7 +21,7 @@ The current options can be grabbed using the `--help` flag.
|
|
21
21
|
|
22
22
|
```
|
23
23
|
$ redis-copy --help
|
24
|
-
redis-copy v0.0.
|
24
|
+
redis-copy v0.0.5
|
25
25
|
Usage: redis-copy [options] <source> <destination>
|
26
26
|
<source> and <destination> must be redis connection uris
|
27
27
|
like [redis://]<hostname>[:<port>][/<db>]
|
@@ -31,13 +31,14 @@ Specific options:
|
|
31
31
|
auto: uses new if available, otherwise fallback
|
32
32
|
new: use redis DUMP and RESTORE commands (faster)
|
33
33
|
classic: migrates via multiple type-specific commands
|
34
|
+
--[no-]pipeline Use redis pipeline where available (default true)
|
35
|
+
-d, --[no-]debug Write debug output (default false)
|
36
|
+
-t, --[no-]trace Enable backtrace on failure (default false)
|
37
|
+
-f, --[no-]fail-fast Abort on first failure (default false)
|
38
|
+
--[no-]verify [PERCENT] Verify percentage of transfers -- VERY SLOW (default 0)
|
39
|
+
--[no-]prompt Prompt for confirmation (default true)
|
40
|
+
--[no-]allow-nonempty Allow non-empty destination (default false)
|
34
41
|
--[no-]dry-run Output configuration and exit
|
35
|
-
-d, --[no-]debug Write debug output
|
36
|
-
-t, --[no-]trace Enable backtrace on failure
|
37
|
-
-f, --[no-]fail-fast Abort on first failure
|
38
|
-
-y, --yes Automatically accept any prompts
|
39
|
-
--[no-]allow-nonempty Allow non-empty destination
|
40
|
-
|
41
42
|
```
|
42
43
|
|
43
44
|
## Example:
|
data/lib/redis-copy.rb
CHANGED
@@ -45,6 +45,17 @@ module RedisCopy
|
|
45
45
|
ui.notify("FAIL: #{key.dump}")
|
46
46
|
ui.abort if options[:fail_fast]
|
47
47
|
end
|
48
|
+
|
49
|
+
probably(options[:verify]) do
|
50
|
+
if strategem.verify?(key)
|
51
|
+
stats[:verified] += 1
|
52
|
+
else
|
53
|
+
stats[:borked] += 1
|
54
|
+
ui.notify("BORK: #{key.dump}")
|
55
|
+
ui.abort if options[:fail_fast]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
48
59
|
ui.notify("PROGRESS: #{stats.inspect}") if (stats[:attempt] % 1000).zero?
|
49
60
|
end.tap do |stats|
|
50
61
|
ui.notify("DONE: #{stats.inspect}")
|
@@ -53,6 +64,17 @@ module RedisCopy
|
|
53
64
|
|
54
65
|
private
|
55
66
|
|
67
|
+
# yields the block, probably.
|
68
|
+
# @param probability [Integer] 0-100
|
69
|
+
# @return [void]
|
70
|
+
# used by ::copy in determining whether to verify.
|
71
|
+
def probably(probability)
|
72
|
+
return if probability.zero?
|
73
|
+
if probability >= 100 || (Random::rand(100) % 100) < probability
|
74
|
+
yield
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
56
78
|
def same_redis?(redis_a, redis_b)
|
57
79
|
# Redis::Client#id returns the connection uri
|
58
80
|
# e.g. 'redis://localhost:6379/0'
|
data/lib/redis-copy/cli.rb
CHANGED
@@ -10,10 +10,12 @@ module RedisCopy
|
|
10
10
|
ui: :command_line,
|
11
11
|
key_emitter: :default,
|
12
12
|
strategy: :auto,
|
13
|
+
verify: 0,
|
13
14
|
pipeline: :true,
|
14
15
|
fail_fast: false,
|
15
16
|
prompt: true,
|
16
17
|
trace: false,
|
18
|
+
debug: false,
|
17
19
|
allow_nonempty: false,
|
18
20
|
}.freeze unless defined?(DEFAULTS)
|
19
21
|
|
@@ -64,8 +66,21 @@ module RedisCopy
|
|
64
66
|
options[:fail_fast] = ff
|
65
67
|
end
|
66
68
|
|
67
|
-
opts.on('--[no-]
|
68
|
-
|
69
|
+
opts.on('--[no-]verify [PERCENT]',
|
70
|
+
"Verify percentage of transfers -- VERY SLOW (default #{DEFAULTS[:verify]})"
|
71
|
+
) do |verify|
|
72
|
+
options[:verify] = case verify
|
73
|
+
when /\A1?[0-9]{2}\z/
|
74
|
+
verify.to_i
|
75
|
+
when false, 'false', 'none'
|
76
|
+
0
|
77
|
+
else
|
78
|
+
100
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.on('--[no-]prompt', "Prompt for confirmation (default #{DEFAULTS[:prompt]})") do |prompt|
|
83
|
+
options[:prompt] = prompt
|
69
84
|
end
|
70
85
|
|
71
86
|
opts.on('--[no-]allow-nonempty', "Allow non-empty destination (default #{DEFAULTS[:allow_nonempty]})") do |allow_nonempty|
|
@@ -28,6 +28,10 @@ module RedisCopy
|
|
28
28
|
raise NotImplementedError
|
29
29
|
end
|
30
30
|
|
31
|
+
def dbsize
|
32
|
+
@redis.dbsize
|
33
|
+
end
|
34
|
+
|
31
35
|
def to_s
|
32
36
|
self.class.name.demodulize.humanize
|
33
37
|
end
|
@@ -36,10 +40,16 @@ module RedisCopy
|
|
36
40
|
class Default
|
37
41
|
include KeyEmitter
|
38
42
|
|
39
|
-
def
|
40
|
-
|
43
|
+
def keys
|
44
|
+
dbsize = self.dbsize
|
45
|
+
|
46
|
+
# HT: http://stackoverflow.com/a/11466770
|
47
|
+
dbsize_str = dbsize.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
48
|
+
|
49
|
+
@ui.abort unless (dbsize < 10_000) || (@ui.confirm? <<-EOWARNING.strip_heredoc)
|
41
50
|
WARNING: #{self} key emitter uses redis.keys('*') to
|
42
|
-
get its list of keys
|
51
|
+
get its list of keys, and you have #{dbsize_str} keys in
|
52
|
+
your source DB.
|
43
53
|
|
44
54
|
The redis keys command [reference](http://redis.io/commands/keys)
|
45
55
|
says this:
|
@@ -52,10 +62,7 @@ module RedisCopy
|
|
52
62
|
> regular application code. If you're looking for a way to find
|
53
63
|
> keys in a subset of your keyspace, consider using sets.
|
54
64
|
EOWARNING
|
55
|
-
super
|
56
|
-
end
|
57
65
|
|
58
|
-
def keys
|
59
66
|
@ui.debug "REDIS: #{@redis.client.id} KEYS *"
|
60
67
|
@redis.keys('*').to_enum
|
61
68
|
end
|
data/lib/redis-copy/strategy.rb
CHANGED
@@ -21,6 +21,10 @@ module RedisCopy
|
|
21
21
|
copierklass.new(source, destination, ui, options)
|
22
22
|
end
|
23
23
|
|
24
|
+
def self.included(base)
|
25
|
+
base.send(:include, Verifier)
|
26
|
+
end
|
27
|
+
|
24
28
|
# @param source [Redis]
|
25
29
|
# @param destination [Redis]
|
26
30
|
def initialize(source, destination, ui, options = {})
|
@@ -40,5 +44,46 @@ module RedisCopy
|
|
40
44
|
return super if defined? super
|
41
45
|
raise NotImplementedError
|
42
46
|
end
|
47
|
+
|
48
|
+
def verify?(key)
|
49
|
+
@ui.debug("VERIFY: #{key.dump}")
|
50
|
+
type = @src.type(key)
|
51
|
+
proc = case type
|
52
|
+
when 'string' then proc { |r| r.get key }
|
53
|
+
when 'hash' then proc { |r| r.hgetall key }
|
54
|
+
when 'zset' then proc { |r| r.zrange(key, 0, -1, :with_scores => true) }
|
55
|
+
when 'set' then proc { |r| r.smembers(key).sort }
|
56
|
+
when 'list' then proc { |r| r.lrange(key, 0, -1) }
|
57
|
+
else
|
58
|
+
@ui.debug("BORK: #{key.dump} has unknown type #{type.dump}!")
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
|
62
|
+
return false unless same_response?(&proc)
|
63
|
+
return false unless same_response? { |r| r.ttl key }
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def same_response?(&blk)
|
71
|
+
responses = {
|
72
|
+
source: capture_result(@src, &blk),
|
73
|
+
destination: capture_result(@dst, &blk)
|
74
|
+
}
|
75
|
+
if (responses[:source] == responses[:destination])
|
76
|
+
return true
|
77
|
+
else
|
78
|
+
@ui.debug("MISMATCH: #{keys.dump} #{responses.inspect}")
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def capture_result(redis, &block)
|
84
|
+
return [:returned, block.call(redis)]
|
85
|
+
rescue Object => exception
|
86
|
+
return [:raised, exception]
|
87
|
+
end
|
43
88
|
end
|
44
89
|
end
|
data/lib/redis-copy/ui.rb
CHANGED
data/lib/redis-copy/version.rb
CHANGED
@@ -6,9 +6,11 @@ describe RedisCopy::KeyEmitter::Default do
|
|
6
6
|
let(:ui) { double.as_null_object }
|
7
7
|
let(:instance) { RedisCopy::KeyEmitter::Default.new(redis, ui)}
|
8
8
|
let(:connection_uri) { 'redis://12.34.56.78:9000/15' }
|
9
|
+
let(:key_count) { 100_000 }
|
9
10
|
|
10
11
|
before(:each) do
|
11
12
|
redis.stub_chain('client.id').and_return(connection_uri)
|
13
|
+
redis.stub(:dbsize) { key_count }
|
12
14
|
ui.stub(:debug).with(anything)
|
13
15
|
end
|
14
16
|
|
@@ -28,6 +30,22 @@ describe RedisCopy::KeyEmitter::Default do
|
|
28
30
|
exactly(:once)
|
29
31
|
instance.keys
|
30
32
|
end
|
33
|
+
context 'when source has > 10,000 keys' do
|
34
|
+
let(:key_count) { 100_000 }
|
35
|
+
it 'should ask for confirmation' do
|
36
|
+
ui.should_receive(:confirm?) do |confirmation|
|
37
|
+
confirmation.should match /\b100,000\b/
|
38
|
+
end
|
39
|
+
instance.keys
|
40
|
+
end
|
41
|
+
end
|
42
|
+
context 'when source has <= 10,000 keys' do
|
43
|
+
let(:key_count) { 1_000 }
|
44
|
+
it 'should not ask for confirmation' do
|
45
|
+
ui.should_not_receive(:confirm?)
|
46
|
+
instance.keys
|
47
|
+
end
|
48
|
+
end
|
31
49
|
end
|
32
50
|
end
|
33
51
|
end
|
@@ -31,60 +31,76 @@ class RedisMultiplex < Struct.new(:source, :destination)
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
shared_examples_for(:no_ttl) do
|
35
|
+
# key, redis,
|
36
|
+
subject { redis.ttl(key) }
|
37
|
+
it { should eq -1 }
|
38
|
+
end
|
39
|
+
|
40
|
+
shared_examples_for(:ttl_set) do
|
41
|
+
# key, redis, ttl
|
42
|
+
subject { redis.ttl(key) }
|
43
|
+
it { should eq ttl }
|
44
|
+
end
|
45
|
+
|
46
|
+
shared_examples_for '#verify?' do
|
47
|
+
before(:each) do
|
48
|
+
ui.stub(:debug).and_call_original
|
49
|
+
ui.stub(:notify) do |message|
|
50
|
+
puts message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
it 'should verify successfully' do
|
54
|
+
strategy.verify?(key).should be_true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
34
58
|
shared_examples_for(RedisCopy::Strategy) do
|
35
59
|
let(:key) { rand(16**128).to_s(16) }
|
36
60
|
after(:each) { multiplex.both { |redis| redis.del(key) } }
|
61
|
+
let(:ttl) { 100 }
|
37
62
|
|
38
63
|
context '#copy' do
|
64
|
+
before(:each) { populate.call }
|
39
65
|
context 'string' do
|
40
66
|
let(:source_string) { rand(16**256).to_s(16) }
|
41
|
-
|
67
|
+
let(:populate) { proc {source.set(key, source_string)} }
|
42
68
|
[true,false].each do |with_expiry|
|
43
69
|
context "with_expiry(#{with_expiry})" do
|
44
|
-
before(:each) { source.expire(key,
|
70
|
+
before(:each) { source.expire(key, ttl) } if with_expiry
|
45
71
|
context 'before' do
|
46
72
|
context 'source' do
|
73
|
+
let(:redis) { source }
|
47
74
|
subject { source.get(key) }
|
48
75
|
it { should_not be_nil }
|
49
76
|
it { should eq source_string }
|
50
|
-
|
51
|
-
subject { source.ttl(key) }
|
52
|
-
it { should eq 100 } if with_expiry
|
53
|
-
it { should eq -1 } unless with_expiry
|
54
|
-
end
|
77
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
55
78
|
end
|
56
79
|
context 'destination' do
|
80
|
+
let(:redis) { destination }
|
57
81
|
subject { destination.get(key) }
|
58
82
|
it { should be_nil }
|
59
|
-
|
60
|
-
subject { destination.ttl(key) }
|
61
|
-
it { should eq -1 }
|
62
|
-
end
|
83
|
+
it_should_behave_like :no_ttl
|
63
84
|
end
|
64
85
|
end
|
65
86
|
|
66
87
|
context 'after' do
|
67
88
|
before(:each) { strategy.copy(key) }
|
68
89
|
context 'source' do
|
90
|
+
let(:redis) { source }
|
69
91
|
subject { source.get(key) }
|
70
92
|
it { should_not be_nil }
|
71
93
|
it { should eq source_string }
|
72
|
-
|
73
|
-
subject { source.ttl(key) }
|
74
|
-
it { should eq 100 } if with_expiry
|
75
|
-
it { should eq -1 } unless with_expiry
|
76
|
-
end
|
94
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
77
95
|
end
|
78
96
|
context 'destination' do
|
97
|
+
let(:redis) { destination }
|
79
98
|
subject { destination.get(key) }
|
80
99
|
it { should_not be_nil }
|
81
100
|
it { should eq source_string }
|
82
|
-
|
83
|
-
subject { destination.ttl(key) }
|
84
|
-
it { should eq 100 } if with_expiry
|
85
|
-
it { should eq -1 } unless with_expiry
|
86
|
-
end
|
101
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
87
102
|
end
|
103
|
+
it_should_behave_like '#verify?'
|
88
104
|
end
|
89
105
|
end
|
90
106
|
end
|
@@ -94,53 +110,43 @@ shared_examples_for(RedisCopy::Strategy) do
|
|
94
110
|
let(:source_list) do
|
95
111
|
%w(foo bar baz buz bingo jango)
|
96
112
|
end
|
97
|
-
|
113
|
+
let(:populate) { proc { source_list.each{|x| source.rpush(key, x)} } }
|
98
114
|
[true,false].each do |with_expiry|
|
99
115
|
context "with_expiry(#{with_expiry})" do
|
100
116
|
before(:each) { source.expire(key, 100) } if with_expiry
|
101
117
|
context 'before' do
|
102
118
|
context 'source' do
|
119
|
+
let(:redis) { source }
|
103
120
|
subject { source.lrange(key, 0, -1) }
|
104
121
|
it { should_not be_empty }
|
105
122
|
it { should eq source_list }
|
106
|
-
|
107
|
-
subject { source.ttl(key) }
|
108
|
-
it { should eq 100 } if with_expiry
|
109
|
-
it { should eq -1 } unless with_expiry
|
110
|
-
end
|
123
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
111
124
|
end
|
112
125
|
context 'destination' do
|
126
|
+
let(:redis) { destination }
|
113
127
|
subject { destination.lrange(key, 0, -1) }
|
114
128
|
it { should be_empty }
|
115
|
-
|
116
|
-
subject { destination.ttl(key) }
|
117
|
-
it { should eq -1 }
|
118
|
-
end
|
129
|
+
it_should_behave_like :no_ttl
|
119
130
|
end
|
120
131
|
end
|
121
132
|
|
122
133
|
context 'after' do
|
123
134
|
before(:each) { strategy.copy(key) }
|
124
135
|
context 'source' do
|
136
|
+
let(:redis) { source }
|
125
137
|
subject { source.lrange(key, 0, -1) }
|
126
138
|
it { should_not be_empty }
|
127
139
|
it { should eq source_list }
|
128
|
-
|
129
|
-
subject { source.ttl(key) }
|
130
|
-
it { should eq 100 } if with_expiry
|
131
|
-
it { should eq -1 } unless with_expiry
|
132
|
-
end
|
140
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
133
141
|
end
|
134
142
|
context 'destination' do
|
143
|
+
let(:redis) { destination }
|
135
144
|
subject { destination.lrange(key, 0, -1) }
|
136
145
|
it { should_not be_empty }
|
137
146
|
it { should eq source_list }
|
138
|
-
|
139
|
-
subject { destination.ttl(key) }
|
140
|
-
it { should eq 100 } if with_expiry
|
141
|
-
it { should eq -1 } unless with_expiry
|
142
|
-
end
|
147
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
143
148
|
end
|
149
|
+
it_should_behave_like '#verify?'
|
144
150
|
end
|
145
151
|
end
|
146
152
|
end
|
@@ -150,52 +156,41 @@ shared_examples_for(RedisCopy::Strategy) do
|
|
150
156
|
let(:source_list) do
|
151
157
|
%w(foo bar baz buz bingo jango)
|
152
158
|
end
|
153
|
-
|
159
|
+
let(:populate) { proc { source_list.each{|x| source.sadd(key, x)} } }
|
154
160
|
[true,false].each do |with_expiry|
|
155
161
|
context "with_expiry(#{with_expiry})" do
|
156
162
|
before(:each) { source.expire(key, 100) } if with_expiry
|
157
163
|
context 'before' do
|
158
164
|
context 'source' do
|
165
|
+
let(:redis) { source }
|
159
166
|
subject { source.smembers(key) }
|
160
167
|
it { should_not be_empty }
|
161
168
|
it { should =~ source_list }
|
162
|
-
|
163
|
-
subject { source.ttl(key) }
|
164
|
-
it { should eq 100 } if with_expiry
|
165
|
-
it { should eq -1 } unless with_expiry
|
166
|
-
end
|
169
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
167
170
|
end
|
168
171
|
context 'destination' do
|
172
|
+
let(:redis) { destination }
|
169
173
|
subject { destination.smembers(key) }
|
170
174
|
it { should be_empty }
|
171
|
-
|
172
|
-
subject { destination.ttl(key) }
|
173
|
-
it { should eq -1 }
|
174
|
-
end
|
175
|
+
it_should_behave_like :no_ttl
|
175
176
|
end
|
176
177
|
end
|
177
178
|
|
178
179
|
context 'after' do
|
179
180
|
before(:each) { strategy.copy(key) }
|
180
181
|
context 'source' do
|
182
|
+
let(:redis) { source }
|
181
183
|
subject { source.smembers(key) }
|
182
184
|
it { should_not be_empty }
|
183
185
|
it { should =~ source_list }
|
184
|
-
|
185
|
-
subject { source.ttl(key) }
|
186
|
-
it { should eq 100 } if with_expiry
|
187
|
-
it { should eq -1 } unless with_expiry
|
188
|
-
end
|
186
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
189
187
|
end
|
190
188
|
context 'destination' do
|
189
|
+
let(:redis) { destination }
|
191
190
|
subject { destination.smembers(key) }
|
192
191
|
it { should_not be_empty }
|
193
192
|
it { should =~ source_list }
|
194
|
-
|
195
|
-
subject { destination.ttl(key) }
|
196
|
-
it { should eq 100 } if with_expiry
|
197
|
-
it { should eq -1 } unless with_expiry
|
198
|
-
end
|
193
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
199
194
|
end
|
200
195
|
end
|
201
196
|
end
|
@@ -209,53 +204,43 @@ shared_examples_for(RedisCopy::Strategy) do
|
|
209
204
|
'baz' => 'buz'
|
210
205
|
}
|
211
206
|
end
|
212
|
-
|
207
|
+
let(:populate) { proc { source.mapped_hmset(key, source_hash) } }
|
213
208
|
[true,false].each do |with_expiry|
|
214
209
|
context "with_expiry(#{with_expiry})" do
|
215
210
|
before(:each) { source.expire(key, 100) } if with_expiry
|
216
211
|
context 'before' do
|
217
212
|
context 'source' do
|
213
|
+
let(:redis) { source }
|
218
214
|
subject { source.hgetall(key) }
|
219
215
|
it { should_not be_empty }
|
220
216
|
it { should eq source_hash }
|
221
|
-
|
222
|
-
subject { source.ttl(key) }
|
223
|
-
it { should eq 100 } if with_expiry
|
224
|
-
it { should eq -1 } unless with_expiry
|
225
|
-
end
|
217
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
226
218
|
end
|
227
219
|
context 'destination' do
|
220
|
+
let(:redis) { destination }
|
228
221
|
subject { destination.hgetall(key) }
|
229
222
|
it { should be_empty }
|
230
|
-
|
231
|
-
subject { destination.ttl(key) }
|
232
|
-
it { should eq -1 }
|
233
|
-
end
|
223
|
+
it_should_behave_like :no_ttl
|
234
224
|
end
|
235
225
|
end
|
236
226
|
|
237
227
|
context 'after' do
|
238
228
|
before(:each) { strategy.copy(key) }
|
239
229
|
context 'source' do
|
230
|
+
let(:redis) { source }
|
240
231
|
subject { source.hgetall(key) }
|
241
232
|
it { should_not be_empty }
|
242
233
|
it { should eq source_hash }
|
243
|
-
|
244
|
-
subject { source.ttl(key) }
|
245
|
-
it { should eq 100 } if with_expiry
|
246
|
-
it { should eq -1 } unless with_expiry
|
247
|
-
end
|
234
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
248
235
|
end
|
249
236
|
context 'destination' do
|
237
|
+
let(:redis) { destination }
|
250
238
|
subject { destination.hgetall(key) }
|
251
239
|
it { should_not be_empty }
|
252
240
|
it { should eq source_hash }
|
253
|
-
|
254
|
-
subject { destination.ttl(key) }
|
255
|
-
it { should eq 100 } if with_expiry
|
256
|
-
it { should eq -1 } unless with_expiry
|
257
|
-
end
|
241
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
258
242
|
end
|
243
|
+
it_should_behave_like '#verify?'
|
259
244
|
end
|
260
245
|
end
|
261
246
|
end
|
@@ -272,53 +257,43 @@ shared_examples_for(RedisCopy::Strategy) do
|
|
272
257
|
end
|
273
258
|
let(:vs_source_zset) { source_zset.to_a }
|
274
259
|
let(:sv_source_zset) { vs_source_zset.map(&:reverse) }
|
275
|
-
|
260
|
+
let(:populate) { proc { source.zadd(key, sv_source_zset) } }
|
276
261
|
[true,false].each do |with_expiry|
|
277
262
|
context "with_expiry(#{with_expiry})" do
|
278
263
|
before(:each) { source.expire(key, 100) } if with_expiry
|
279
264
|
context 'before' do
|
280
265
|
context 'source' do
|
266
|
+
let(:redis) { source }
|
281
267
|
subject { source.zrange(key, 0, -1, :with_scores => true) }
|
282
268
|
it { should_not be_empty }
|
283
269
|
it { should =~ vs_source_zset }
|
284
|
-
|
285
|
-
subject { source.ttl(key) }
|
286
|
-
it { should eq 100 } if with_expiry
|
287
|
-
it { should eq -1 } unless with_expiry
|
288
|
-
end
|
270
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
289
271
|
end
|
290
272
|
context 'destination' do
|
273
|
+
let(:redis) { destination }
|
291
274
|
subject { destination.zrange(key, 0, -1, :with_scores => true) }
|
292
275
|
it { should be_empty }
|
293
|
-
|
294
|
-
subject { destination.ttl(key) }
|
295
|
-
it { should eq -1 }
|
296
|
-
end
|
276
|
+
it_should_behave_like :no_ttl
|
297
277
|
end
|
298
278
|
end
|
299
279
|
|
300
280
|
context 'after' do
|
301
281
|
before(:each) { strategy.copy(key) }
|
302
282
|
context 'source' do
|
283
|
+
let(:redis) { source }
|
303
284
|
subject { source.zrange(key, 0, -1, :with_scores => true) }
|
304
285
|
it { should_not be_empty }
|
305
286
|
it { should =~ vs_source_zset }
|
306
|
-
|
307
|
-
subject { source.ttl(key) }
|
308
|
-
it { should eq 100 } if with_expiry
|
309
|
-
it { should eq -1 } unless with_expiry
|
310
|
-
end
|
287
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
311
288
|
end
|
312
289
|
context 'destination' do
|
290
|
+
let(:redis) { destination }
|
313
291
|
subject { destination.zrange(key, 0, -1, :with_scores => true) }
|
314
292
|
it { should_not be_empty }
|
315
293
|
it { should =~ vs_source_zset }
|
316
|
-
|
317
|
-
subject { destination.ttl(key) }
|
318
|
-
it { should eq 100 } if with_expiry
|
319
|
-
it { should eq -1 } unless with_expiry
|
320
|
-
end
|
294
|
+
it_should_behave_like (with_expiry ? :ttl_set : :no_ttl)
|
321
295
|
end
|
296
|
+
it_should_behave_like '#verify?'
|
322
297
|
end
|
323
298
|
end
|
324
299
|
end
|
@@ -328,7 +303,8 @@ end
|
|
328
303
|
|
329
304
|
describe RedisCopy::Strategy do
|
330
305
|
let(:options) { Hash.new } # append using before(:each) { options.update(foo: true) }
|
331
|
-
let(:ui) { double.as_null_object }
|
306
|
+
# let(:ui) { double.as_null_object }
|
307
|
+
let(:ui) { RedisCopy::UI::CommandLine.new(options) }
|
332
308
|
let(:strategy) { strategy_class.new(source, destination, ui, options)}
|
333
309
|
let(:multiplex) { RedisMultiplex.new(source, destination) }
|
334
310
|
let(:source) { Redis.new(db: 14) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-copy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|