redis-copy 0.0.3 → 0.0.5
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/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
|