redis-copy 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -14,4 +14,5 @@ rdoc
14
14
  spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
+ spec/db
17
18
  tmp
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ script: "bundle exec rake run"
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ gemfile:
7
+ - .travis/Gemfile.redis-gem-3.0
8
+ - .travis/Gemfile.redis-gem-master
9
+ env:
10
+ global:
11
+ - TIMEOUT=1
12
+ matrix:
13
+ - REDIS_BRANCH=2.4
14
+ - REDIS_BRANCH=2.6
15
+ - REDIS_BRANCH=2.8
16
+ - REDIS_BRANCH=unstable
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ source 'https://rubygems.org'
3
+
4
+ gem 'redis', '~> 3.0'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,44 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ redis-copy (0.0.5)
5
+ activesupport
6
+ redis
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.1)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ atomic (1.1.14)
18
+ diff-lcs (1.2.5)
19
+ i18n (0.6.5)
20
+ minitest (4.7.5)
21
+ multi_json (1.8.2)
22
+ rake (10.1.0)
23
+ redis (3.0.5)
24
+ rspec (2.14.1)
25
+ rspec-core (~> 2.14.0)
26
+ rspec-expectations (~> 2.14.0)
27
+ rspec-mocks (~> 2.14.0)
28
+ rspec-core (2.14.7)
29
+ rspec-expectations (2.14.4)
30
+ diff-lcs (>= 1.1.3, < 2.0)
31
+ rspec-mocks (2.14.4)
32
+ thread_safe (0.1.3)
33
+ atomic
34
+ tzinfo (0.3.38)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ bundler (~> 1.3)
41
+ rake
42
+ redis (~> 3.0)
43
+ redis-copy!
44
+ rspec (~> 2.14)
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ source 'https://rubygems.org'
3
+
4
+ gem 'redis', :git => 'https://github.com/redis/redis-rb.git'
5
+
6
+ gemspec :path => '../'
@@ -0,0 +1,49 @@
1
+ GIT
2
+ remote: https://github.com/redis/redis-rb.git
3
+ revision: 9bb4156dcd43105b68e24e5efc579dddb12cf260
4
+ specs:
5
+ redis (3.0.6)
6
+
7
+ PATH
8
+ remote: ../
9
+ specs:
10
+ redis-copy (0.0.5)
11
+ activesupport
12
+ redis
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ activesupport (4.0.1)
18
+ i18n (~> 0.6, >= 0.6.4)
19
+ minitest (~> 4.2)
20
+ multi_json (~> 1.3)
21
+ thread_safe (~> 0.1)
22
+ tzinfo (~> 0.3.37)
23
+ atomic (1.1.14)
24
+ diff-lcs (1.2.5)
25
+ i18n (0.6.5)
26
+ minitest (4.7.5)
27
+ multi_json (1.8.2)
28
+ rake (10.1.0)
29
+ rspec (2.14.1)
30
+ rspec-core (~> 2.14.0)
31
+ rspec-expectations (~> 2.14.0)
32
+ rspec-mocks (~> 2.14.0)
33
+ rspec-core (2.14.7)
34
+ rspec-expectations (2.14.4)
35
+ diff-lcs (>= 1.1.3, < 2.0)
36
+ rspec-mocks (2.14.4)
37
+ thread_safe (0.1.3)
38
+ atomic
39
+ tzinfo (0.3.38)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ bundler (~> 1.3)
46
+ rake
47
+ redis!
48
+ redis-copy!
49
+ rspec (~> 2.14)
data/README.md CHANGED
@@ -24,7 +24,7 @@ $ redis-copy --help
24
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
- like [redis://]<hostname>[:<port>][/<db>]
27
+ like [redis://][<username>:<password>@]<hostname>[:<port>][/<db>]
28
28
 
29
29
  Specific options:
30
30
  --strategy STRATEGY Select strategy (auto, new, classic) (default auto)
data/Rakefile CHANGED
@@ -7,3 +7,63 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
7
7
  spec.pattern = FileList['spec/**/*_spec.rb']
8
8
  spec.verbose = true
9
9
  end
10
+
11
+
12
+ # STOLEN NEARLY VERBATIM FROM MIT-LICENSED REDIS-RB
13
+ # https://github.com/redis/redis-rb
14
+ require 'rubygems'
15
+
16
+ ENV["REDIS_BRANCH"] ||= "unstable"
17
+
18
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
19
+ require 'redis/version'
20
+
21
+ REDIS_DIR = File.expand_path(File.join("..", "spec"), __FILE__)
22
+ REDIS_CNF = File.join(REDIS_DIR, "redis.spec.conf")
23
+ REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
24
+ BINARY = "tmp/redis-#{ENV["REDIS_BRANCH"]}/src/redis-server"
25
+
26
+ task :default => :run
27
+
28
+ desc "Run tests and manage server start/stop"
29
+ task :run => [:start, :spec, :stop]
30
+
31
+ desc "Start the Redis server"
32
+ task :start => BINARY do
33
+ sh "#{BINARY} --version"
34
+
35
+ redis_running = \
36
+ begin
37
+ File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
38
+ rescue Errno::ESRCH
39
+ FileUtils.rm REDIS_PID
40
+ false
41
+ end
42
+
43
+ unless redis_running
44
+ unless system("#{BINARY} #{REDIS_CNF}")
45
+ abort "could not start redis-server"
46
+ end
47
+ end
48
+ end
49
+
50
+ desc "Stop the Redis server"
51
+ task :stop do
52
+ if File.exists?(REDIS_PID)
53
+ Process.kill "INT", File.read(REDIS_PID).to_i
54
+ FileUtils.rm REDIS_PID
55
+ end
56
+ end
57
+
58
+ file BINARY do
59
+ branch = ENV.fetch("REDIS_BRANCH")
60
+
61
+ sh <<-SH
62
+ mkdir -p tmp;
63
+ cd tmp;
64
+ wget https://github.com/antirez/redis/archive/#{branch}.tar.gz -O #{branch}.tar.gz;
65
+ tar xf #{branch}.tar.gz;
66
+ cd redis-#{branch};
67
+ make
68
+ SH
69
+ end
@@ -93,8 +93,13 @@ module RedisCopy
93
93
  host = uri.host
94
94
  port = uri.port if uri.port
95
95
  db = uri.path ? uri.path[1..-1].to_i : 0
96
+ password = uri.password
96
97
 
97
- Redis.new(host: host, port: port, db: db)
98
+ # Connect & Ping to ensure access.
99
+ Redis.new(host: host, port: port, db: db, password: password).tap(&:ping)
100
+ rescue Redis::CommandError => e
101
+ fail(Redis::CommandError,
102
+ "There was a problem connecting to #{uri.to_s}\n#{e.message}")
98
103
  end
99
104
  end
100
105
  end
@@ -5,10 +5,10 @@ require 'optparse'
5
5
 
6
6
  module RedisCopy
7
7
  class CLI
8
- REDIS_URI = (/\A(?:redis:\/\/)?([a-z0-9\-.]+)(:[0-9]{1,5})?(\/(?:(?:1[0-5])|[0-9]))?\z/i).freeze
8
+ REDIS_URI = (/\A(?:redis:\/\/)?(\w*:\w+@)?([a-z0-9\-.]+)(:[0-9]{1,5})?(\/(?:(?:1[0-5])|[0-9]))?\z/i).freeze
9
9
  DEFAULTS = {
10
10
  ui: :command_line,
11
- key_emitter: :default,
11
+ key_emitter: :auto,
12
12
  strategy: :auto,
13
13
  verify: 0,
14
14
  pipeline: :true,
@@ -25,7 +25,7 @@ module RedisCopy
25
25
 
26
26
  OptionParser.new do |opts|
27
27
  opts.version = RedisCopy::VERSION
28
- opts.banner = "#{opts.program_name} v#{opts.version}\n" +
28
+ opts.banner = "#{opts.program_name} v#{opts.version} (with redis-rb #{Redis::VERSION})\n" +
29
29
  "Usage: #{opts.program_name} [options] <source> <destination>"
30
30
 
31
31
  indent_desc = proc do |desc|
@@ -33,7 +33,7 @@ module RedisCopy
33
33
  end
34
34
 
35
35
  opts.separator " <source> and <destination> must be redis connection uris"
36
- opts.separator " like [redis://]<hostname>[:<port>][/<db>]"
36
+ opts.separator " like [redis://][<username>:<password>@]<hostname>[:<port>][/<db>]"
37
37
  opts.separator ''
38
38
  opts.separator "Specific options:"
39
39
 
@@ -48,6 +48,17 @@ module RedisCopy
48
48
  options[:strategy] = strategy
49
49
  end
50
50
 
51
+ opts.on('--emitter EMITTER', [:auto, :scan, :keys],
52
+ indent_desc.(
53
+ "Select key emitter (auto, keys, scan) (default #{DEFAULTS[:strategy]})\n" +
54
+ " auto: uses scan if available, otherwise fallback\n" +
55
+ " scan: use redis SCAN command (faster, less blocking)\n" +
56
+ " keys: uses redis KEYS command (dangerous, esp. on large datasets)"
57
+ )
58
+ ) do |emitter|
59
+ options[:key_emitter] = emitter
60
+ end
61
+
51
62
  opts.on('--[no-]pipeline',
52
63
  "Use redis pipeline where available (default #{DEFAULTS[:pipeline]})"
53
64
  ) do |pipeline|
@@ -9,9 +9,15 @@ module RedisCopy
9
9
  module KeyEmitter
10
10
  def self.load(redis, ui, options = {})
11
11
  key_emitter = options.fetch(:key_emitter, :default)
12
- const_name = key_emitter.to_s.camelize
13
- require "redis-copy/key-emitter/#{key_emitter}" unless const_defined?(const_name)
14
- const_get(const_name).new(redis, ui, options)
12
+ scan_compatible = Scan::compatible?(redis)
13
+ emitklass = case key_emitter
14
+ when :keys then Keys
15
+ when :scan
16
+ raise ArgumentError unless scan_compatible
17
+ Scan
18
+ when :auto then scan_compatible ? Scan : Keys
19
+ end
20
+ emitklass.new(redis, ui, options)
15
21
  end
16
22
 
17
23
  # @param redis [Redis]
@@ -37,7 +43,7 @@ module RedisCopy
37
43
  end
38
44
 
39
45
  # The default strategy blindly uses `redis.keys('*')`
40
- class Default
46
+ class Keys
41
47
  include KeyEmitter
42
48
 
43
49
  def keys
@@ -66,6 +72,27 @@ module RedisCopy
66
72
  @ui.debug "REDIS: #{@redis.client.id} KEYS *"
67
73
  @redis.keys('*').to_enum
68
74
  end
75
+
76
+ def self.compatible?(redis)
77
+ true
78
+ end
79
+ end
80
+
81
+ class Scan
82
+ include KeyEmitter
83
+
84
+ def keys
85
+ @redis.scan_each(count: 1000)
86
+ end
87
+
88
+ def self.compatible?(redis)
89
+ bin_version = Gem::Version.new(redis.info['redis_version'])
90
+ bin_requirement = Gem::Requirement.new('>= 2.7.105')
91
+
92
+ return false unless bin_requirement.satisfied_by?(bin_version)
93
+
94
+ redis.respond_to?(:scan_each)
95
+ end
69
96
  end
70
97
  end
71
98
  end
@@ -1,5 +1,36 @@
1
1
  # encoding: utf-8
2
2
 
3
+ # The Classic strategy borrows *heavily* from the utils/redis-copy.rb found
4
+ # in Redis source, which is also the inspiration for this gem.
5
+ #
6
+ # #{REDIS}/utils/redis-copy.rb - Copyright © 2009-2010 Salvatore Sanfilippo.
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions
11
+ # are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright
14
+ # notice, this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright
16
+ # notice, this list of conditions and the following disclaimer in
17
+ # the documentation and/or other materials provided with the
18
+ # distribution.
19
+ # * Neither the name of Redis nor the names of its contributors may
20
+ # be used to endorse or promote products derived from this software
21
+ # without specific prior written permission.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
+ # POSSIBILITY OF SUCH DAMAGE.
3
34
  module RedisCopy
4
35
  module Strategy
5
36
  class Classic
@@ -51,7 +82,11 @@ module RedisCopy
51
82
  vs_zset = @src.zrange(key, 0, -1, :with_scores => true)
52
83
  sv_zset = vs_zset.map(&:reverse)
53
84
  @dst.zadd(key, sv_zset)
85
+ when 'none'
86
+ @ui.warn("GONE: #{key.dump}")
87
+ return false
54
88
  else
89
+ @ui.warn("UNKNOWN(#{vtype}): #{key.dump}")
55
90
  return false
56
91
  end
57
92
 
@@ -71,6 +106,10 @@ module RedisCopy
71
106
  def pipeline_enabled?
72
107
  @pipeline_enabled ||= (false | @opt[:pipeline])
73
108
  end
109
+
110
+ def self.compatible?(redis)
111
+ true
112
+ end
74
113
  end
75
114
  end
76
115
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module RedisCopy
4
- VERSION = '0.0.5'
4
+ VERSION = '0.0.6'
5
5
  end
@@ -8,13 +8,12 @@ Gem::Specification.new do |spec|
8
8
  spec.name = 'redis-copy'
9
9
  spec.version = RedisCopy::VERSION
10
10
 
11
- authors_and_emails = [['Ryan Biesemeyer', 'ryan@yaauie.com']]
12
- # authors_and_emails = (`sh git shortlog -sne`).lines.map do |l|
13
- # (/(?<=\t)(.+) <(.+)>\z/).match(l.chomp).last(2)
14
- # end.compact.map(&:to_a)
11
+ authors_and_emails = (`git shortlog -sne`).lines.map do |l|
12
+ (/(?<=\t)(.+) <(.+)>\z/).match(l.chomp).to_a.last(2)
13
+ end.compact.map(&:to_a)
15
14
 
16
15
  spec.authors = authors_and_emails.map(&:first)
17
- spec.email = authors_and_emails.map(&:last)
16
+ spec.email = ['redis-copy@yaauie.com']
18
17
  spec.summary = 'Copy the contents of one redis db to another'
19
18
  spec.description = 'A command-line utility built for copying the ' +
20
19
  'contents of one redis db to another over a ' +
@@ -1,51 +1,67 @@
1
1
  # encoding: utf-8
2
2
  require 'redis-copy'
3
+ require_relative '../spec_helper.rb'
3
4
 
4
- describe RedisCopy::KeyEmitter::Default do
5
- let(:redis) { double }
5
+ shared_examples_for RedisCopy::KeyEmitter do
6
+ let(:emitter_klass) { described_class }
7
+ let(:redis) { Redis.new(REDIS_OPTIONS) }
6
8
  let(:ui) { double.as_null_object }
7
- let(:instance) { RedisCopy::KeyEmitter::Default.new(redis, ui)}
8
- let(:connection_uri) { 'redis://12.34.56.78:9000/15' }
9
- let(:key_count) { 100_000 }
9
+ let(:instance) { emitter_klass.new(redis, ui)}
10
+ let(:key_count) { 1 }
11
+ let(:keys) { key_count.times.map{|i| i.to_s(16) } }
10
12
 
11
13
  before(:each) do
12
- redis.stub_chain('client.id').and_return(connection_uri)
13
- redis.stub(:dbsize) { key_count }
14
+ unless emitter_klass.compatible?(redis)
15
+ pending "#{emitter_klass} not supported in your environment"
16
+ end
17
+ key_count.times.each_slice(50) do |keys|
18
+ kv = keys.map{|x| x.to_s(16)}.zip(keys)
19
+ redis.mset(*kv.flatten)
20
+ end
14
21
  ui.stub(:debug).with(anything)
15
22
  end
23
+ after(:each) { redis.flushdb }
16
24
 
17
25
  context '#keys' do
18
- let(:mock_return) { ['foo:bar', 'asdf:qwer'] }
19
- before(:each) do
20
- redis.should_receive(:keys).with('*').exactly(:once).and_return(mock_return)
21
- end
26
+ let(:key_count) { 64 }
22
27
  context 'the result' do
23
28
  subject { instance.keys }
24
- its(:to_a) { should eq mock_return }
29
+ its(:to_a) { should =~ keys }
25
30
  end
26
- context 'the supplied ui' do
27
- it 'should get a debug message' do
28
- ui.should_receive(:debug).
29
- with(/#{Regexp.escape(connection_uri)} KEYS \*/).
30
- exactly(:once)
31
- instance.keys
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
31
+ end
32
+ end
33
+
34
+ describe RedisCopy::KeyEmitter::Keys do
35
+ it_should_behave_like RedisCopy::KeyEmitter do
36
+ context '#keys' do
37
+ context 'the supplied ui' do
38
+ it 'should get a debug message' do
39
+ ui.should_receive(:debug).
40
+ with(/#{redis.client.id} KEYS \*/).
41
+ exactly(:once)
39
42
  instance.keys
40
43
  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
44
+ context 'when source has > 10,000 keys' do
45
+ let(:key_count) { 10_001 }
46
+ it 'should ask for confirmation' do
47
+ ui.should_receive(:confirm?) do |confirmation|
48
+ confirmation.should match /\b10,001/
49
+ end
50
+ instance.keys
51
+ end
52
+ end
53
+ context 'when source has <= 10,000 keys' do
54
+ let(:key_count) { 1_000 }
55
+ it 'should not ask for confirmation' do
56
+ ui.should_not_receive(:confirm?)
57
+ instance.keys
58
+ end
47
59
  end
48
60
  end
49
61
  end
50
62
  end
51
63
  end
64
+
65
+ describe RedisCopy::KeyEmitter::Scan do
66
+ it_should_behave_like RedisCopy::KeyEmitter
67
+ end
@@ -1,40 +1,10 @@
1
1
  # encoding: utf-8
2
- class RedisMultiplex < Struct.new(:source, :destination)
3
- ResponseError = Class.new(RuntimeError)
4
-
5
- def ensure_same!(&blk)
6
- responses = {
7
- source: capture_result(source, &blk),
8
- destination: capture_result(destination, &blk)
9
- }
10
- unless responses[:source] == responses[:destination]
11
- raise ResponseError.new(responses.to_s)
12
- end
13
- case responses[:destination].first
14
- when :raised then raise responses[:destination].last
15
- when :returned then return responses[:destination].last
16
- end
17
- end
18
- alias_method :both!, :ensure_same!
19
-
20
- def both(&blk)
21
- both!(&blk)
22
- true
23
- rescue ResponseError
24
- false
25
- end
26
-
27
- def capture_result(redis, &block)
28
- return [:returned, block.call(redis)]
29
- rescue Object => exception
30
- return [:raised, exception]
31
- end
32
- end
2
+ require_relative '../spec_helper'
33
3
 
34
4
  shared_examples_for(:no_ttl) do
35
5
  # key, redis,
36
6
  subject { redis.ttl(key) }
37
- it { should eq -1 }
7
+ it { should be < 0 }
38
8
  end
39
9
 
40
10
  shared_examples_for(:ttl_set) do
@@ -56,10 +26,25 @@ shared_examples_for '#verify?' do
56
26
  end
57
27
 
58
28
  shared_examples_for(RedisCopy::Strategy) do
29
+ let(:strategy_class) { described_class }
30
+ let(:options) { Hash.new } # append using before(:each) { options.update(foo: true) }
31
+ # let(:ui) { double.as_null_object }
32
+ let(:ui) { RedisCopy::UI::CommandLine.new(options) }
33
+ let(:strategy) { strategy_class.new(source, destination, ui, options)}
34
+ let(:multiplex) { RedisMultiplex.new(source, destination) }
35
+ let(:source) { Redis.new(REDIS_OPTIONS.merge(db: 14)) }
36
+ let(:destination) { Redis.new(REDIS_OPTIONS.merge(db: 15)) }
37
+
59
38
  let(:key) { rand(16**128).to_s(16) }
60
39
  after(:each) { multiplex.both { |redis| redis.del(key) } }
61
40
  let(:ttl) { 100 }
62
41
 
42
+ before(:each) do
43
+ unless [source, destination].all?{|redis| strategy_class.compatible?(redis) }
44
+ pending "#{strategy_class} not supported in your environment"
45
+ end
46
+ end
47
+
63
48
  context '#copy' do
64
49
  before(:each) { populate.call }
65
50
  context 'string' do
@@ -301,22 +286,13 @@ shared_examples_for(RedisCopy::Strategy) do
301
286
  end
302
287
  end
303
288
 
304
- describe RedisCopy::Strategy do
305
- let(:options) { Hash.new } # append using before(:each) { options.update(foo: true) }
306
- # let(:ui) { double.as_null_object }
307
- let(:ui) { RedisCopy::UI::CommandLine.new(options) }
308
- let(:strategy) { strategy_class.new(source, destination, ui, options)}
309
- let(:multiplex) { RedisMultiplex.new(source, destination) }
310
- let(:source) { Redis.new(db: 14) }
311
- let(:destination) { Redis.new(db: 15) }
312
289
 
313
- describe :New do
314
- let(:strategy_class) { RedisCopy::Strategy::New }
315
- it_should_behave_like RedisCopy::Strategy
316
- end
317
- describe :Classic do
318
- let(:strategy_class) { RedisCopy::Strategy::Classic }
319
- it_should_behave_like RedisCopy::Strategy
290
+ describe RedisCopy::Strategy::New do
291
+ it_should_behave_like RedisCopy::Strategy
292
+ end
293
+
294
+ describe RedisCopy::Strategy::Classic do
295
+ it_should_behave_like RedisCopy::Strategy do
320
296
  context '#maybe_pipeline' do
321
297
  it 'should not pipeline' do
322
298
  source.should_not_receive(:pipelined)
@@ -0,0 +1,9 @@
1
+ dir ./spec/db
2
+ pidfile ./redis.pid
3
+ port 6381
4
+ unixsocket ./redis.sock
5
+ timeout 300
6
+ loglevel debug
7
+ logfile stdout
8
+ databases 16
9
+ daemonize yes
@@ -0,0 +1,38 @@
1
+ REDIS_PORT = 6381
2
+ REDIS_OPTIONS = {
3
+ :port => REDIS_PORT,
4
+ :db => 14,
5
+ :timeout => Float(ENV["TIMEOUT"] || 0.1),
6
+ }
7
+
8
+ class RedisMultiplex < Struct.new(:source, :destination)
9
+ ResponseError = Class.new(RuntimeError)
10
+
11
+ def ensure_same!(&blk)
12
+ responses = {
13
+ source: capture_result(source, &blk),
14
+ destination: capture_result(destination, &blk)
15
+ }
16
+ unless responses[:source] == responses[:destination]
17
+ raise ResponseError.new(responses.to_s)
18
+ end
19
+ case responses[:destination].first
20
+ when :raised then raise responses[:destination].last
21
+ when :returned then return responses[:destination].last
22
+ end
23
+ end
24
+ alias_method :both!, :ensure_same!
25
+
26
+ def both(&blk)
27
+ both!(&blk)
28
+ true
29
+ rescue ResponseError
30
+ false
31
+ end
32
+
33
+ def capture_result(redis, &block)
34
+ return [:returned, block.call(redis)]
35
+ rescue Object => exception
36
+ return [:raised, exception]
37
+ end
38
+ end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-copy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ryan Biesemeyer
9
+ - Denis Goeury
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-11-01 00:00:00.000000000 Z
13
+ date: 2013-11-13 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: bundler
@@ -95,13 +96,18 @@ description: A command-line utility built for copying the contents of one redis
95
96
  to another over a network. Supports all data types, persists ttls, and attempts
96
97
  to be as efficient as possible.
97
98
  email:
98
- - ryan@yaauie.com
99
+ - redis-copy@yaauie.com
99
100
  executables:
100
101
  - redis-copy
101
102
  extensions: []
102
103
  extra_rdoc_files: []
103
104
  files:
104
105
  - .gitignore
106
+ - .travis.yml
107
+ - .travis/Gemfile.redis-gem-3.0
108
+ - .travis/Gemfile.redis-gem-3.0.lock
109
+ - .travis/Gemfile.redis-gem-master
110
+ - .travis/Gemfile.redis-gem-master.lock
105
111
  - Gemfile
106
112
  - LICENSE.txt
107
113
  - README.md
@@ -120,8 +126,11 @@ files:
120
126
  - lib/redis-copy/version.rb
121
127
  - redis-copy.gemspec
122
128
  - redis-copy_spec.rb
129
+ - spec/db/.gitkeep
123
130
  - spec/redis-copy/key-emitter_spec.rb
124
131
  - spec/redis-copy/strategy_spec.rb
132
+ - spec/redis.spec.conf
133
+ - spec/spec_helper.rb
125
134
  homepage: https://github.com/yaauie/redis-copy
126
135
  licenses:
127
136
  - MIT
@@ -135,12 +144,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
144
  - - ! '>='
136
145
  - !ruby/object:Gem::Version
137
146
  version: '0'
147
+ segments:
148
+ - 0
149
+ hash: 2506344141455451823
138
150
  required_rubygems_version: !ruby/object:Gem::Requirement
139
151
  none: false
140
152
  requirements:
141
153
  - - ! '>='
142
154
  - !ruby/object:Gem::Version
143
155
  version: '0'
156
+ segments:
157
+ - 0
158
+ hash: 2506344141455451823
144
159
  requirements: []
145
160
  rubyforge_project:
146
161
  rubygems_version: 1.8.24
@@ -148,6 +163,8 @@ signing_key:
148
163
  specification_version: 3
149
164
  summary: Copy the contents of one redis db to another
150
165
  test_files:
166
+ - spec/db/.gitkeep
151
167
  - spec/redis-copy/key-emitter_spec.rb
152
168
  - spec/redis-copy/strategy_spec.rb
153
- has_rdoc:
169
+ - spec/redis.spec.conf
170
+ - spec/spec_helper.rb