lhm-shopify 3.3.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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +34 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +183 -0
  5. data/.travis.yml +21 -0
  6. data/CHANGELOG.md +216 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +27 -0
  9. data/README.md +284 -0
  10. data/Rakefile +22 -0
  11. data/bin/.gitkeep +0 -0
  12. data/dbdeployer/config.json +32 -0
  13. data/dbdeployer/install.sh +64 -0
  14. data/dev.yml +20 -0
  15. data/gemfiles/ar-2.3_mysql.gemfile +6 -0
  16. data/gemfiles/ar-3.2_mysql.gemfile +5 -0
  17. data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
  18. data/gemfiles/ar-4.0_mysql2.gemfile +5 -0
  19. data/gemfiles/ar-4.1_mysql2.gemfile +5 -0
  20. data/gemfiles/ar-4.2_mysql2.gemfile +5 -0
  21. data/gemfiles/ar-5.0_mysql2.gemfile +5 -0
  22. data/lhm.gemspec +34 -0
  23. data/lib/lhm.rb +131 -0
  24. data/lib/lhm/atomic_switcher.rb +52 -0
  25. data/lib/lhm/chunk_finder.rb +32 -0
  26. data/lib/lhm/chunk_insert.rb +51 -0
  27. data/lib/lhm/chunker.rb +87 -0
  28. data/lib/lhm/cleanup/current.rb +74 -0
  29. data/lib/lhm/command.rb +48 -0
  30. data/lib/lhm/entangler.rb +117 -0
  31. data/lib/lhm/intersection.rb +51 -0
  32. data/lib/lhm/invoker.rb +98 -0
  33. data/lib/lhm/locked_switcher.rb +74 -0
  34. data/lib/lhm/migration.rb +43 -0
  35. data/lib/lhm/migrator.rb +237 -0
  36. data/lib/lhm/printer.rb +59 -0
  37. data/lib/lhm/railtie.rb +9 -0
  38. data/lib/lhm/sql_helper.rb +77 -0
  39. data/lib/lhm/sql_retry.rb +61 -0
  40. data/lib/lhm/table.rb +121 -0
  41. data/lib/lhm/table_name.rb +23 -0
  42. data/lib/lhm/test_support.rb +35 -0
  43. data/lib/lhm/throttler.rb +36 -0
  44. data/lib/lhm/throttler/slave_lag.rb +145 -0
  45. data/lib/lhm/throttler/threads_running.rb +53 -0
  46. data/lib/lhm/throttler/time.rb +29 -0
  47. data/lib/lhm/timestamp.rb +11 -0
  48. data/lib/lhm/version.rb +6 -0
  49. data/shipit.rubygems.yml +0 -0
  50. data/spec/.lhm.example +4 -0
  51. data/spec/README.md +58 -0
  52. data/spec/fixtures/bigint_table.ddl +4 -0
  53. data/spec/fixtures/composite_primary_key.ddl +7 -0
  54. data/spec/fixtures/custom_primary_key.ddl +6 -0
  55. data/spec/fixtures/destination.ddl +6 -0
  56. data/spec/fixtures/lines.ddl +7 -0
  57. data/spec/fixtures/origin.ddl +6 -0
  58. data/spec/fixtures/permissions.ddl +5 -0
  59. data/spec/fixtures/small_table.ddl +4 -0
  60. data/spec/fixtures/tracks.ddl +5 -0
  61. data/spec/fixtures/users.ddl +14 -0
  62. data/spec/fixtures/wo_id_int_column.ddl +6 -0
  63. data/spec/integration/atomic_switcher_spec.rb +93 -0
  64. data/spec/integration/chunk_insert_spec.rb +29 -0
  65. data/spec/integration/chunker_spec.rb +185 -0
  66. data/spec/integration/cleanup_spec.rb +136 -0
  67. data/spec/integration/entangler_spec.rb +66 -0
  68. data/spec/integration/integration_helper.rb +237 -0
  69. data/spec/integration/invoker_spec.rb +33 -0
  70. data/spec/integration/lhm_spec.rb +585 -0
  71. data/spec/integration/lock_wait_timeout_spec.rb +30 -0
  72. data/spec/integration/locked_switcher_spec.rb +50 -0
  73. data/spec/integration/sql_retry/lock_wait_spec.rb +125 -0
  74. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +101 -0
  75. data/spec/integration/table_spec.rb +91 -0
  76. data/spec/test_helper.rb +32 -0
  77. data/spec/unit/atomic_switcher_spec.rb +31 -0
  78. data/spec/unit/chunk_finder_spec.rb +73 -0
  79. data/spec/unit/chunk_insert_spec.rb +44 -0
  80. data/spec/unit/chunker_spec.rb +166 -0
  81. data/spec/unit/entangler_spec.rb +124 -0
  82. data/spec/unit/intersection_spec.rb +51 -0
  83. data/spec/unit/lhm_spec.rb +29 -0
  84. data/spec/unit/locked_switcher_spec.rb +51 -0
  85. data/spec/unit/migrator_spec.rb +146 -0
  86. data/spec/unit/printer_spec.rb +97 -0
  87. data/spec/unit/sql_helper_spec.rb +32 -0
  88. data/spec/unit/table_name_spec.rb +39 -0
  89. data/spec/unit/table_spec.rb +47 -0
  90. data/spec/unit/throttler/slave_lag_spec.rb +317 -0
  91. data/spec/unit/throttler/threads_running_spec.rb +64 -0
  92. data/spec/unit/throttler_spec.rb +124 -0
  93. data/spec/unit/unit_helper.rb +13 -0
  94. metadata +239 -0
@@ -0,0 +1,11 @@
1
+ module Lhm
2
+ class Timestamp
3
+ def initialize(time)
4
+ @time = time
5
+ end
6
+
7
+ def to_s
8
+ @time.strftime "%Y_%m_%d_%H_%M_%S_#{ '%03d' % (@time.usec / 1000) }"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ module Lhm
5
+ VERSION = '3.3.5'
6
+ end
File without changes
data/spec/.lhm.example ADDED
@@ -0,0 +1,4 @@
1
+ mysqldir=/usr/local/mysql
2
+ basedir=~/lhm-cluster
3
+ master_port=3306
4
+ slave_port=3307
data/spec/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Preparing for master slave integration tests
2
+
3
+ ## Configuration
4
+
5
+ create ~/.lhm:
6
+
7
+ mysqldir=/usr/local/mysql
8
+ basedir=~/lhm-cluster
9
+ master_port=3306
10
+ slave_port=3307
11
+
12
+ mysqldir specifies the location of your mysql install. basedir is the
13
+ directory master and slave databases will get installed into.
14
+
15
+ ## Automatic setup
16
+
17
+ ### Run
18
+
19
+ bin/lhm-spec-clobber.sh
20
+
21
+ You can set the integration specs up to run against a master slave setup by
22
+ running the included that. This deletes the configured lhm master slave setup and reinstalls and configures a master slave setup.
23
+
24
+ Follow the manual instructions if you want more control over this process.
25
+
26
+ ## Manual setup
27
+
28
+ ### set up instances
29
+
30
+ bin/lhm-spec-setup-cluster.sh
31
+
32
+ ### start instances
33
+
34
+ basedir=/opt/lhm-luster
35
+ mysqld --defaults-file="$basedir/master/my.cnf"
36
+ mysqld --defaults-file="$basedir/slave/my.cnf"
37
+
38
+ ### run the grants
39
+
40
+ bin/lhm-spec-grants.sh
41
+
42
+ ## run specs
43
+
44
+ Setup the dependency gems
45
+
46
+ export BUNDLE_GEMFILE=gemfiles/ar-4.2_mysql2.gemfile
47
+ bundle install
48
+
49
+ To run specs in slave mode, set the MASTER_SLAVE=1 when running tests:
50
+
51
+ MASTER_SLAVE=1 bundle exec rake specs
52
+
53
+ # connecting
54
+
55
+ you can connect by running (with the respective ports):
56
+
57
+ mysql --protocol=TCP -p3307
58
+
@@ -0,0 +1,4 @@
1
+ CREATE TABLE `bigint_table` (
2
+ `id` BIGINT(20) auto_increment,
3
+ PRIMARY KEY (`id`)
4
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,7 @@
1
+
2
+ CREATE TABLE `composite_primary_key` (
3
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
4
+ `shop_id` bigint(20) NOT NULL,
5
+ PRIMARY KEY (`shop_id`,`id`),
6
+ INDEX `index_key_id` (`id`)
7
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,6 @@
1
+ CREATE TABLE `custom_primary_key` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `pk` varchar(255),
4
+ PRIMARY KEY (`pk`),
5
+ UNIQUE KEY `index_custom_primary_key_on_id` (`id`)
6
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,6 @@
1
+ CREATE TABLE `destination` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `destination` int(11) DEFAULT NULL,
4
+ `common` varchar(255) DEFAULT NULL,
5
+ PRIMARY KEY (`id`)
6
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,7 @@
1
+ CREATE TABLE `lines` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `between` varchar(10),
4
+ `lines` int(11),
5
+ `key` varchar(10),
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,6 @@
1
+ CREATE TABLE `origin` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `origin` int(11) DEFAULT NULL,
4
+ `common` varchar(255) DEFAULT NULL,
5
+ PRIMARY KEY (`id`)
6
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,5 @@
1
+ CREATE TABLE `permissions` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `track_id` int(11) DEFAULT NULL,
4
+ PRIMARY KEY (`id`)
5
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,4 @@
1
+ CREATE TABLE `small_table` (
2
+ `id` INT(11) auto_increment,
3
+ PRIMARY KEY (`id`)
4
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,5 @@
1
+ CREATE TABLE `tracks` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `public` int(4) DEFAULT 0,
4
+ PRIMARY KEY (`id`)
5
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,14 @@
1
+ CREATE TABLE `users` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `reference` int(11) DEFAULT NULL,
4
+ `username` varchar(255) DEFAULT NULL,
5
+ `group` varchar(255) DEFAULT 'Superfriends',
6
+ `created_at` datetime DEFAULT NULL,
7
+ `comment` varchar(20) DEFAULT NULL,
8
+ `description` text,
9
+ PRIMARY KEY (`id`),
10
+ UNIQUE KEY `index_users_on_reference` (`reference`),
11
+ KEY `index_users_on_username_and_created_at` (`username`,`created_at`),
12
+ KEY `index_with_a_custom_name` (`username`,`group`)
13
+
14
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,6 @@
1
+ -- Without id int column
2
+ CREATE TABLE `wo_id_int_column` (
3
+ `id` varchar(15) NOT NULL,
4
+ PRIMARY KEY (`id`)
5
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
6
+
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/atomic_switcher'
9
+
10
+ describe Lhm::AtomicSwitcher do
11
+ include IntegrationHelper
12
+
13
+ before(:each) { connect_master! }
14
+
15
+ describe 'switching' do
16
+ before(:each) do
17
+ Thread.abort_on_exception = true
18
+ @origin = table_create('origin')
19
+ @destination = table_create('destination')
20
+ @migration = Lhm::Migration.new(@origin, @destination)
21
+ @logs = StringIO.new
22
+ Lhm.logger = Logger.new(@logs)
23
+ @connection.execute('SET GLOBAL innodb_lock_wait_timeout=3')
24
+ @connection.execute('SET GLOBAL lock_wait_timeout=3')
25
+ end
26
+
27
+ after(:each) do
28
+ Thread.abort_on_exception = false
29
+ end
30
+
31
+ it 'should retry and log on lock wait timeouts' do
32
+ connection = mock()
33
+ connection.stubs(:data_source_exists?).returns(true)
34
+ connection.stubs(:execute).raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.').then.returns(true)
35
+
36
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection, retriable: {base_interval: 0})
37
+
38
+ assert switcher.run
39
+
40
+ log_messages = @logs.string.split("\n")
41
+ assert_equal(2, log_messages.length)
42
+ assert log_messages[0].include? "Starting run of class=Lhm::AtomicSwitcher"
43
+ assert log_messages[1].include? "[AtomicSwitcher] ActiveRecord::StatementInvalid: 'Lock wait timeout exceeded; try restarting transaction.' - 1 tries"
44
+ end
45
+
46
+ it 'should give up on lock wait timeouts after a configured number of tries' do
47
+ connection = mock()
48
+ connection.stubs(:data_source_exists?).returns(true)
49
+ connection.stubs(:execute).twice.raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.')
50
+
51
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection, retriable: {tries: 2, base_interval: 0})
52
+
53
+ assert_raises(ActiveRecord::StatementInvalid) { switcher.run }
54
+ end
55
+
56
+ it 'should raise on non lock wait timeout exceptions' do
57
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection)
58
+ switcher.send :define_singleton_method, :atomic_switch do
59
+ 'SELECT * FROM nonexistent'
60
+ end
61
+ -> { switcher.run }.must_raise(ActiveRecord::StatementInvalid)
62
+ end
63
+
64
+ it "should raise when destination doesn't exist" do
65
+ connection = mock()
66
+ connection.stubs(:data_source_exists?).returns(false)
67
+
68
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection)
69
+
70
+ assert_raises(Lhm::Error) { switcher.run }
71
+ end
72
+
73
+ it 'rename origin to archive' do
74
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection)
75
+ switcher.run
76
+
77
+ slave do
78
+ data_source_exists?(@origin).must_equal true
79
+ table_read(@migration.archive_name).columns.keys.must_include 'origin'
80
+ end
81
+ end
82
+
83
+ it 'rename destination to origin' do
84
+ switcher = Lhm::AtomicSwitcher.new(@migration, connection)
85
+ switcher.run
86
+
87
+ slave do
88
+ data_source_exists?(@destination).must_equal false
89
+ table_read(@origin.name).columns.keys.must_include 'destination'
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
2
+ require 'lhm/migration'
3
+
4
+ describe Lhm::ChunkInsert do
5
+ include IntegrationHelper
6
+
7
+ describe 'insert_and_return_count_of_rows_created' do
8
+ before(:each) do
9
+ connect_master!
10
+ @origin = table_create(:origin)
11
+ @destination = table_create(:destination)
12
+ @migration = Lhm::Migration.new(@origin, @destination)
13
+ execute("insert into origin set id = 1001")
14
+ @instance = Lhm::ChunkInsert.new(@migration, connection, 1001, 1001)
15
+ end
16
+
17
+ it "returns the count" do
18
+ assert_equal 1, @instance.insert_and_return_count_of_rows_created
19
+ end
20
+
21
+ it "inserts the record into the slave" do
22
+ @instance.insert_and_return_count_of_rows_created
23
+
24
+ slave do
25
+ count_all(@destination.name).must_equal(1)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,185 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
5
+ require 'lhm/table'
6
+ require 'lhm/migration'
7
+
8
+ describe Lhm::Chunker do
9
+ include IntegrationHelper
10
+
11
+ before(:each) { connect_master! }
12
+
13
+ describe 'copying' do
14
+ before(:each) do
15
+ @origin = table_create(:origin)
16
+ @destination = table_create(:destination)
17
+ @migration = Lhm::Migration.new(@origin, @destination)
18
+ end
19
+
20
+ it 'should copy 1 row from origin to destination even if the id of the single row does not start at 1' do
21
+ execute("insert into origin set id = 1001 ")
22
+ printer = Lhm::Printer::Base.new
23
+
24
+ def printer.notify(*) ;end
25
+ def printer.end(*) [] ;end
26
+
27
+ Lhm::Chunker.new(@migration, connection, {:throttler => Lhm::Throttler::Time.new(:stride => 100), :printer => printer} ).run
28
+
29
+ slave do
30
+ count_all(@destination.name).must_equal(1)
31
+ end
32
+
33
+ end
34
+
35
+ it 'should create the modified destination, even if the source is empty' do
36
+ execute("truncate origin ")
37
+ printer = Lhm::Printer::Base.new
38
+
39
+ def printer.notify(*) ;end
40
+ def printer.end(*) [] ;end
41
+
42
+ Lhm::Chunker.new(@migration, connection, {:throttler => Lhm::Throttler::Time.new(:stride => 100), :printer => printer} ).run
43
+
44
+ slave do
45
+ count_all(@destination.name).must_equal(0)
46
+ end
47
+
48
+ end
49
+
50
+ it 'should copy 23 rows from origin to destination in one shot, regardless of the value of the id' do
51
+ 23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
52
+
53
+ printer = MiniTest::Mock.new
54
+ printer.expect(:notify, :return_value, [Integer, Integer])
55
+ printer.expect(:end, :return_value, [])
56
+
57
+ Lhm::Chunker.new(
58
+ @migration, connection, { :throttler => Lhm::Throttler::Time.new(:stride => 100), :printer => printer }
59
+ ).run
60
+
61
+ slave do
62
+ count_all(@destination.name).must_equal(23)
63
+ end
64
+
65
+ printer.verify
66
+
67
+ end
68
+
69
+ it 'should copy all the records of a table, even if the last chunk starts with the last record of it.' do
70
+ 11.times { |n| execute("insert into origin set id = '#{ n + 1 }'") }
71
+
72
+ printer = Lhm::Printer::Base.new
73
+
74
+ def printer.notify(*) ;end
75
+ def printer.end(*) [] ;end
76
+
77
+ Lhm::Chunker.new(
78
+ @migration, connection, { :throttler => Lhm::Throttler::Time.new(:stride => 10), :printer => printer }
79
+ ).run
80
+
81
+ slave do
82
+ count_all(@destination.name).must_equal(11)
83
+ end
84
+
85
+ end
86
+
87
+ it 'should copy 23 rows from origin to destination in one shot with slave lag based throttler, regardless of the value of the id' do
88
+ 23.times { |n| execute("insert into origin set id = '#{ 100000 + n * n + 23 }'") }
89
+
90
+ printer = MiniTest::Mock.new
91
+ printer.expect(:notify, :return_value, [Integer, Integer])
92
+ printer.expect(:end, :return_value, [])
93
+
94
+ Lhm::Chunker.new(
95
+ @migration, connection, { :throttler => Lhm::Throttler::SlaveLag.new(:stride => 100), :printer => printer }
96
+ ).run
97
+
98
+ slave do
99
+ count_all(@destination.name).must_equal(23)
100
+ end
101
+
102
+ printer.verify
103
+ end
104
+
105
+ it 'should throttle work stride based on slave lag' do
106
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
107
+
108
+ printer = mock()
109
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
110
+ printer.expects(:end)
111
+
112
+ throttler = Lhm::Throttler::SlaveLag.new(:stride => 10, :allowed_lag => 0)
113
+ def throttler.max_current_slave_lag
114
+ 1
115
+ end
116
+
117
+ Lhm::Chunker.new(
118
+ @migration, connection, { :throttler => throttler, :printer => printer }
119
+ ).run
120
+
121
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
122
+
123
+ slave do
124
+ count_all(@destination.name).must_equal(15)
125
+ end
126
+ end
127
+
128
+ it 'should detect a single slave with no lag in the default configuration' do
129
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
130
+
131
+ printer = mock()
132
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
133
+ printer.expects(:verify)
134
+ printer.expects(:end)
135
+
136
+ throttler = Lhm::Throttler::SlaveLag.new(:stride => 10, :allowed_lag => 0)
137
+
138
+ def throttler.slave_hosts
139
+ ['127.0.0.1']
140
+ end
141
+
142
+ if master_slave_mode?
143
+ def throttler.slave_connection(slave)
144
+ config = ActiveRecord::Base.connection_pool.spec.config.dup
145
+ config[:host] = slave
146
+ config[:port] = 3307
147
+ ActiveRecord::Base.send('mysql2_connection', config)
148
+ end
149
+ end
150
+
151
+ Lhm::Chunker.new(
152
+ @migration, connection, { :throttler => throttler, :printer => printer }
153
+ ).run
154
+
155
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT, throttler.timeout_seconds)
156
+ assert_equal(0, throttler.send(:max_current_slave_lag))
157
+
158
+ slave do
159
+ count_all(@destination.name).must_equal(15)
160
+ end
161
+
162
+ printer.verify
163
+ end
164
+
165
+ it 'should abort early if the triggers are removed' do
166
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
167
+
168
+ printer = mock()
169
+
170
+ failer = Proc.new { false }
171
+
172
+ exception = assert_raises do
173
+ Lhm::Chunker.new(
174
+ @migration, connection, { :verifier => failer, :printer => printer, :throttler => Lhm::Throttler::Time.new(:stride => 100) }
175
+ ).run
176
+ end
177
+
178
+ assert_match "Verification failed, aborting early", exception.message
179
+
180
+ slave do
181
+ count_all(@destination.name).must_equal(0)
182
+ end
183
+ end
184
+ end
185
+ end