lhm 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,106 +6,115 @@ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
6
6
  require 'lhm/table'
7
7
  require 'lhm/migration'
8
8
  require 'lhm/chunker'
9
+ require 'lhm/throttler'
9
10
 
10
11
  describe Lhm::Chunker do
11
12
  include UnitHelper
12
13
 
13
14
  before(:each) do
14
- @origin = Lhm::Table.new("origin")
15
- @destination = Lhm::Table.new("destination")
16
- @migration = Lhm::Migration.new(@origin, @destination)
17
- @chunker = Lhm::Chunker.new(@migration, nil, { :start => 1, :limit => 10 })
15
+ @origin = Lhm::Table.new("foo")
16
+ @destination = Lhm::Table.new("bar")
17
+ @migration = Lhm::Migration.new(@origin, @destination)
18
+ @connection = MiniTest::Mock.new
19
+ # This is a poor man's stub
20
+ @throttler = Object.new
21
+ def @throttler.run
22
+ #noop
23
+ end
24
+ def @throttler.stride
25
+ 1
26
+ end
27
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
28
+ :start => 1,
29
+ :limit => 10)
18
30
  end
19
31
 
20
- describe "copy into" do
21
- before(:each) do
22
- @origin.columns["secret"] = { :metadata => "VARCHAR(255)"}
23
- @destination.columns["secret"] = { :metadata => "VARCHAR(255)"}
24
- end
25
-
26
- it "should copy the correct range and column" do
27
- @chunker.copy(from = 1, to = 100).must_equal(
28
- "insert ignore into `destination` (`secret`) " +
29
- "select origin.`secret` from `origin` " +
30
- "where origin.`id` between 1 and 100"
31
- )
32
- end
33
- end
34
-
35
- describe "invalid" do
36
- before do
37
- @chunker = Lhm::Chunker.new(@migration, nil, { :start => 0, :limit => -1 })
38
- end
39
-
40
- it "should have zero chunks" do
41
- @chunker.traversable_chunks_size.must_equal 0
42
- end
43
-
44
- it "should not iterate" do
45
- @chunker.up_to do |bottom, top|
46
- raise "should not iterate"
32
+ describe "#run" do
33
+ it "chunks the result set according to the stride size" do
34
+ def @throttler.stride
35
+ 2
47
36
  end
48
- end
49
- end
50
37
 
51
- describe "one" do
52
- before do
53
- @chunker = Lhm::Chunker.new(@migration, nil, {
54
- :stride => 100_000, :start => 1, :limit => 300_000
55
- })
56
- end
38
+ @connection.expect(:update, 2) do |stmt|
39
+ stmt.first =~ /between 1 and 2/
40
+ end
41
+ @connection.expect(:update, 2) do |stmt|
42
+ stmt.first =~ /between 3 and 4/
43
+ end
44
+ @connection.expect(:update, 2) do |stmt|
45
+ stmt.first =~ /between 5 and 6/
46
+ end
47
+ @connection.expect(:update, 2) do |stmt|
48
+ stmt.first =~ /between 7 and 8/
49
+ end
50
+ @connection.expect(:update, 2) do |stmt|
51
+ stmt.first =~ /between 9 and 10/
52
+ end
57
53
 
58
- it "should have one chunk" do
59
- @chunker.traversable_chunks_size.must_equal 3
54
+ @chunker.run
55
+ @connection.verify
60
56
  end
61
57
 
62
- it "should lower bound chunk on 1" do
63
- @chunker.bottom(chunk = 1).must_equal 1
64
- end
58
+ it "handles stride changes during execution" do
59
+ #roll our own stubbing
60
+ def @throttler.stride
61
+ @run_count ||= 0
62
+ @run_count = @run_count + 1
63
+ if @run_count > 1
64
+ 3
65
+ else
66
+ 2
67
+ end
68
+ end
65
69
 
66
- it "should upper bound chunk on 100" do
67
- @chunker.top(chunk = 1).must_equal 100_000
68
- end
69
- end
70
+ @connection.expect(:update, 2) do |stmt|
71
+ stmt.first =~ /between 1 and 2/
72
+ end
73
+ @connection.expect(:update, 2) do |stmt|
74
+ stmt.first =~ /between 3 and 5/
75
+ end
76
+ @connection.expect(:update, 2) do |stmt|
77
+ stmt.first =~ /between 6 and 8/
78
+ end
79
+ @connection.expect(:update, 2) do |stmt|
80
+ stmt.first =~ /between 9 and 10/
81
+ end
70
82
 
71
- describe "two" do
72
- before do
73
- @chunker = Lhm::Chunker.new(@migration, nil, {
74
- :stride => 100_000, :start => 2, :limit => 150_000
75
- })
83
+ @chunker.run
84
+ @connection.verify
76
85
  end
77
86
 
78
- it "should have two chunks" do
79
- @chunker.traversable_chunks_size.must_equal 2
80
- end
87
+ it "separates filter conditions from chunking conditions" do
88
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
89
+ :start => 1,
90
+ :limit => 2)
91
+ @connection.expect(:update, 1) do |stmt|
92
+ stmt.first =~ /where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/
93
+ end
81
94
 
82
- it "should lower bound second chunk on 100_000" do
83
- @chunker.bottom(chunk = 2).must_equal 100_002
84
- end
95
+ def @migration.conditions
96
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
97
+ end
85
98
 
86
- it "should upper bound second chunk on 150_000" do
87
- @chunker.top(chunk = 2).must_equal 150_000
99
+ @chunker.run
100
+ @connection.verify
88
101
  end
89
- end
90
102
 
91
- describe "iterating" do
92
- before do
93
- @chunker = Lhm::Chunker.new(@migration, nil, {
94
- :stride => 100, :start => 53, :limit => 121
95
- })
96
- end
103
+ it "doesn't mess with inner join filters" do
104
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
105
+ :start => 1,
106
+ :limit => 2)
107
+ @connection.expect(:update, 1) do |stmt|
108
+ puts stmt
109
+ stmt.first =~ /inner join bar on foo.id = bar.foo_id and/
110
+ end
97
111
 
98
- it "should iterate" do
99
- @chunker.up_to do |bottom, top|
100
- bottom.must_equal 53
101
- top.must_equal 121
112
+ def @migration.conditions
113
+ "inner join bar on foo.id = bar.foo_id"
102
114
  end
103
- end
104
- end
105
115
 
106
- describe "throttling" do
107
- it "should default to 100 milliseconds" do
108
- @chunker.throttle_seconds.must_equal 0.1
116
+ @chunker.run
117
+ @connection.verify
109
118
  end
110
119
  end
111
120
  end
@@ -11,6 +11,7 @@ if defined?(DataMapper)
11
11
  before do
12
12
  data_mapper.expect :is_a?, true, [DataMapper::Adapters::AbstractAdapter]
13
13
  data_mapper.expect :options, options
14
+ data_mapper.expect :options, options
14
15
  end
15
16
 
16
17
  after do
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd.
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
4
+
5
+ describe Lhm do
6
+
7
+ before(:each) do
8
+ Lhm.remove_class_variable :@@logger if Lhm.class_variable_defined? :@@logger
9
+ end
10
+
11
+ describe "logger" do
12
+
13
+ it "should use the default parameters if no logger explicitly set" do
14
+ Lhm.logger.must_be_kind_of Logger
15
+ Lhm.logger.level.must_equal Logger::INFO
16
+ Lhm.logger.instance_eval{ @logdev }.dev.must_equal STDOUT
17
+ end
18
+
19
+ it "should use s new logger if set" do
20
+ l = Logger.new('omg.ponies')
21
+ l.level = Logger::ERROR
22
+ Lhm.logger = l
23
+
24
+ Lhm.logger.level.must_equal Logger::ERROR
25
+ Lhm.logger.instance_eval{ @logdev }.dev.must_be_kind_of File
26
+ Lhm.logger.instance_eval{ @logdev }.dev.path.must_equal 'omg.ponies'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,79 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
2
+
3
+ require 'lhm/printer'
4
+
5
+ describe Lhm::Printer do
6
+ include UnitHelper
7
+
8
+ describe "percentage printer" do
9
+
10
+ before(:each) do
11
+ @printer = Lhm::Printer::Percentage.new
12
+ end
13
+
14
+ it "prints the percentage" do
15
+ mock = MiniTest::Mock.new
16
+ 10.times do |i|
17
+ mock.expect(:write, :return_value) do |message|
18
+ assert_match /^\r/, message.first
19
+ assert_match /#{i}\/10/, message.first
20
+ end
21
+ end
22
+
23
+ @printer.instance_variable_set(:@output, mock)
24
+ 10.times { |i| @printer.notify(i, 10) }
25
+ mock.verify
26
+ end
27
+
28
+ it "always print a bigger message" do
29
+ @length = 0
30
+ mock = MiniTest::Mock.new
31
+ 3.times do |i|
32
+ mock.expect(:write, :return_value) do |message|
33
+ assert message.first.length >= @length
34
+ @length = message.first.length
35
+ end
36
+ end
37
+
38
+ @printer.instance_variable_set(:@output, mock)
39
+ @printer.notify(10, 100)
40
+ @printer.notify(0, 100)
41
+ @printer.notify(1, 1000000)
42
+ @printer.notify(0, 0)
43
+ @printer.notify(0, nil)
44
+
45
+ mock.verify
46
+ end
47
+
48
+ it "prints the end message" do
49
+ mock = MiniTest::Mock.new
50
+ mock.expect(:write, :return_value, [String])
51
+ mock.expect(:write, :return_value, ["\n"])
52
+
53
+ @printer.instance_variable_set(:@output, mock)
54
+ @printer.end
55
+
56
+ mock.verify
57
+ end
58
+ end
59
+
60
+ describe "dot printer" do
61
+
62
+ before(:each) do
63
+ @printer = Lhm::Printer::Dot.new
64
+ end
65
+
66
+ it "prints the dots" do
67
+ mock = MiniTest::Mock.new
68
+ 10.times do
69
+ mock.expect(:write, :return_value, ["."])
70
+ end
71
+
72
+ @printer.instance_variable_set(:@output, mock)
73
+ 10.times { @printer.notify }
74
+
75
+ mock.verify
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
2
+
3
+ require 'lhm/throttler'
4
+
5
+ describe Lhm::Throttler do
6
+ include UnitHelper
7
+
8
+ before :each do
9
+ @mock = Class.new do
10
+ extend Lhm::Throttler
11
+ end
12
+ end
13
+
14
+ describe "#setup_throttler" do
15
+ describe "when passing a key" do
16
+ before do
17
+ @mock.setup_throttler(:time_throttler, :delay => 2)
18
+ end
19
+
20
+ it "instantiates the time throttle" do
21
+ @mock.throttler.class.must_equal Lhm::Throttler::Time
22
+ end
23
+
24
+ it "returns 2 seconds as time" do
25
+ @mock.throttler.timeout_seconds.must_equal 2
26
+ end
27
+ end
28
+
29
+ describe "when passing an instance" do
30
+
31
+ before do
32
+ @instance = Class.new(Lhm::Throttler::Time) do
33
+ def timeout_seconds
34
+ 0
35
+ end
36
+ end.new
37
+
38
+ @mock.setup_throttler(@instance)
39
+ end
40
+
41
+ it "returns the instace given" do
42
+ @mock.throttler.must_equal @instance
43
+ end
44
+
45
+ it "returns 0 seconds as time" do
46
+ @mock.throttler.timeout_seconds.must_equal 0
47
+ end
48
+ end
49
+
50
+ describe "when passing a class" do
51
+
52
+ before do
53
+ @klass = Class.new(Lhm::Throttler::Time)
54
+ @mock.setup_throttler(@klass)
55
+ end
56
+
57
+ it "has the same class as given" do
58
+ @mock.throttler.class.must_equal @klass
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "#throttler" do
64
+
65
+ it "returns the default Time based" do
66
+ @mock.throttler.class.must_equal Lhm::Throttler::Time
67
+ end
68
+
69
+ it "should default to 100 milliseconds" do
70
+ @mock.throttler.timeout_seconds.must_equal 0.1
71
+ end
72
+ end
73
+ end
@@ -1,18 +1,6 @@
1
1
  # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
2
  # Schmidt
3
-
4
- require File.expand_path(File.dirname(__FILE__)) + "/../bootstrap"
5
-
6
- begin
7
- require 'active_record'
8
- begin
9
- require 'mysql2'
10
- rescue LoadError
11
- require 'mysql'
12
- end
13
- rescue LoadError
14
- require 'dm-core'
15
- end
3
+ require 'test_helper'
16
4
 
17
5
  module UnitHelper
18
6
  def fixture(name)
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
5
- prerelease:
4
+ version: 2.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - SoundCloud
@@ -12,28 +11,25 @@ authors:
12
11
  autorequire:
13
12
  bindir: bin
14
13
  cert_chain: []
15
- date: 2013-07-10 00:00:00.000000000 Z
14
+ date: 2014-07-31 00:00:00.000000000 Z
16
15
  dependencies:
17
16
  - !ruby/object:Gem::Dependency
18
17
  name: minitest
19
18
  requirement: !ruby/object:Gem::Requirement
20
- none: false
21
19
  requirements:
22
- - - '='
20
+ - - ~>
23
21
  - !ruby/object:Gem::Version
24
- version: 2.10.0
22
+ version: 5.0.8
25
23
  type: :development
26
24
  prerelease: false
27
25
  version_requirements: !ruby/object:Gem::Requirement
28
- none: false
29
26
  requirements:
30
- - - '='
27
+ - - ~>
31
28
  - !ruby/object:Gem::Version
32
- version: 2.10.0
29
+ version: 5.0.8
33
30
  - !ruby/object:Gem::Dependency
34
31
  name: rake
35
32
  requirement: !ruby/object:Gem::Requirement
36
- none: false
37
33
  requirements:
38
34
  - - ! '>='
39
35
  - !ruby/object:Gem::Version
@@ -41,7 +37,6 @@ dependencies:
41
37
  type: :development
42
38
  prerelease: false
43
39
  version_requirements: !ruby/object:Gem::Requirement
44
- none: false
45
40
  requirements:
46
41
  - - ! '>='
47
42
  - !ruby/object:Gem::Version
@@ -82,13 +77,16 @@ files:
82
77
  - lib/lhm/locked_switcher.rb
83
78
  - lib/lhm/migration.rb
84
79
  - lib/lhm/migrator.rb
80
+ - lib/lhm/printer.rb
85
81
  - lib/lhm/sql_helper.rb
86
82
  - lib/lhm/table.rb
83
+ - lib/lhm/throttler.rb
84
+ - lib/lhm/throttler/time.rb
87
85
  - lib/lhm/version.rb
88
86
  - spec/.lhm.example
89
87
  - spec/README.md
90
- - spec/bootstrap.rb
91
88
  - spec/fixtures/destination.ddl
89
+ - spec/fixtures/lines.ddl
92
90
  - spec/fixtures/origin.ddl
93
91
  - spec/fixtures/permissions.ddl
94
92
  - spec/fixtures/small_table.ddl
@@ -102,6 +100,7 @@ files:
102
100
  - spec/integration/lhm_spec.rb
103
101
  - spec/integration/locked_switcher_spec.rb
104
102
  - spec/integration/table_spec.rb
103
+ - spec/test_helper.rb
105
104
  - spec/unit/active_record_connection_spec.rb
106
105
  - spec/unit/atomic_switcher_spec.rb
107
106
  - spec/unit/chunker_spec.rb
@@ -109,40 +108,42 @@ files:
109
108
  - spec/unit/datamapper_connection_spec.rb
110
109
  - spec/unit/entangler_spec.rb
111
110
  - spec/unit/intersection_spec.rb
111
+ - spec/unit/lhm_spec.rb
112
112
  - spec/unit/locked_switcher_spec.rb
113
113
  - spec/unit/migration_spec.rb
114
114
  - spec/unit/migrator_spec.rb
115
+ - spec/unit/printer_spec.rb
115
116
  - spec/unit/sql_helper_spec.rb
116
117
  - spec/unit/table_spec.rb
118
+ - spec/unit/throttler_spec.rb
117
119
  - spec/unit/unit_helper.rb
118
120
  homepage: http://github.com/soundcloud/lhm
119
121
  licenses: []
122
+ metadata: {}
120
123
  post_install_message:
121
124
  rdoc_options: []
122
125
  require_paths:
123
126
  - lib
124
127
  required_ruby_version: !ruby/object:Gem::Requirement
125
- none: false
126
128
  requirements:
127
129
  - - ! '>='
128
130
  - !ruby/object:Gem::Version
129
131
  version: '0'
130
132
  required_rubygems_version: !ruby/object:Gem::Requirement
131
- none: false
132
133
  requirements:
133
134
  - - ! '>='
134
135
  - !ruby/object:Gem::Version
135
136
  version: '0'
136
137
  requirements: []
137
138
  rubyforge_project:
138
- rubygems_version: 1.8.25
139
+ rubygems_version: 2.2.2
139
140
  signing_key:
140
- specification_version: 3
141
+ specification_version: 4
141
142
  summary: online schema changer for mysql
142
143
  test_files:
143
144
  - spec/README.md
144
- - spec/bootstrap.rb
145
145
  - spec/fixtures/destination.ddl
146
+ - spec/fixtures/lines.ddl
146
147
  - spec/fixtures/origin.ddl
147
148
  - spec/fixtures/permissions.ddl
148
149
  - spec/fixtures/small_table.ddl
@@ -156,6 +157,7 @@ test_files:
156
157
  - spec/integration/lhm_spec.rb
157
158
  - spec/integration/locked_switcher_spec.rb
158
159
  - spec/integration/table_spec.rb
160
+ - spec/test_helper.rb
159
161
  - spec/unit/active_record_connection_spec.rb
160
162
  - spec/unit/atomic_switcher_spec.rb
161
163
  - spec/unit/chunker_spec.rb
@@ -163,9 +165,12 @@ test_files:
163
165
  - spec/unit/datamapper_connection_spec.rb
164
166
  - spec/unit/entangler_spec.rb
165
167
  - spec/unit/intersection_spec.rb
168
+ - spec/unit/lhm_spec.rb
166
169
  - spec/unit/locked_switcher_spec.rb
167
170
  - spec/unit/migration_spec.rb
168
171
  - spec/unit/migrator_spec.rb
172
+ - spec/unit/printer_spec.rb
169
173
  - spec/unit/sql_helper_spec.rb
170
174
  - spec/unit/table_spec.rb
175
+ - spec/unit/throttler_spec.rb
171
176
  - spec/unit/unit_helper.rb