lhm 2.0.0 → 2.1.0

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.
@@ -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