lhm-teak 3.6.0 → 3.6.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b84f8808e479b9588bcd03e14dd67ef885b058d2abec860e174a947545ff2af0
4
- data.tar.gz: 644b6c7e3565d091a8a24d05d35d4077c63846c62761ff637ebc754cd1be846a
3
+ metadata.gz: 0cb61bdc9210d61dff6a96cf2975604f7f67d5e6d053acb5b84f1a016d8bd127
4
+ data.tar.gz: eb107d91175df6109f9b0640e7e0191cea7c946da2d64b49f4f95b86cc08e606
5
5
  SHA512:
6
- metadata.gz: 046cf2c7d36a0ca272bcb0b0130bfd115cc3723610b6b4f9e8d74fe9757a96c3ab3a2e0be779810a6d7a2894ca660e518ffb407b21371de60e8f31ec45113c61
7
- data.tar.gz: e60d5ad405e2a7cbc2f9d057a13ad7fc85bd89d0f6472685a5d72a33cc19b8878688e94a8f2968466028db4947cadcdaaa7fb415f442f7140d803d373ee3e21e
6
+ metadata.gz: b59f1aa113f91e6ff974a87b9fb019a6307278302110b3a99ccc58757f299acd99c6eaee3f9d6d19416cfcf6ffd9a8cdfac17a21fa29fe8b7491956eb7921f82
7
+ data.tar.gz: 05f12dcc9e251b77e624c101dd7e032d5c693f293abf2a3e8f34f8679d0997df0203885273aab8bc74f017fec8e0fa24ecf769e9288703d56fbeda7d6f084e8e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lhm-teak (3.6.0)
4
+ lhm-teak (3.6.1)
5
5
  retriable (>= 3.0.0)
6
6
 
7
7
  GEM
data/lib/lhm/chunker.rb CHANGED
@@ -20,7 +20,7 @@ module Lhm
20
20
  def initialize(migration, connection = nil, options = {})
21
21
  @migration = migration
22
22
  @connection = connection
23
- @chunk_finder = options.fetch(:chuck_finder, ChunkFinder).new(migration, connection, options)
23
+ @chunk_finder = options.fetch(:chunk_finder, ChunkFinder).new(migration, connection, options)
24
24
  @options = options
25
25
  @raise_on_warnings = options.fetch(:raise_on_warnings, false)
26
26
  @verifier = options[:verifier]
@@ -0,0 +1,50 @@
1
+ require 'lhm/id_set_chunk_insert'
2
+
3
+ module Lhm
4
+ class IdSetChunkFinder
5
+ LOG_PREFIX = "Chunker"
6
+
7
+ def initialize(migration, connection = nil, options = {})
8
+ @migration = migration
9
+ @connection = connection
10
+ @ids = options[:ids]
11
+ @throttler = options[:throttler]
12
+ @processed_rows = 0
13
+ end
14
+
15
+ def table_empty?
16
+ ids.nil? || ids.empty?
17
+ end
18
+
19
+ def validate
20
+ end
21
+
22
+ def each_chunk
23
+ @processed_rows = 0
24
+ while @processed_rows < ids.length
25
+ next_idx = [@processed_rows + @throttler.stride, @ids.length].min
26
+ ids_to_insert = ids[@processed_rows...next_idx]
27
+ @processed_rows = next_idx
28
+ yield IdSetChunkInsert.new(@migration, @connection, ids_to_insert)
29
+ end
30
+ end
31
+
32
+ def max_rows
33
+ ids.length
34
+ end
35
+
36
+ def processed_rows
37
+ @processed_rows
38
+ end
39
+
40
+ private
41
+
42
+ def ids
43
+ @ids ||= select_ids_from_db
44
+ end
45
+
46
+ def select_ids_from_db
47
+ @connection.select_values("select id from `#{ @migration.origin_name }` order by id asc", should_retry: true, log_prefix: LOG_PREFIX)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ require 'lhm/sql_retry'
2
+ require 'lhm/proxysql_helper'
3
+
4
+ module Lhm
5
+ class IdSetChunkInsert
6
+
7
+ LOG_PREFIX = "ChunkInsert"
8
+
9
+ def initialize(migration, connection, ids, retry_options = {})
10
+ @migration = migration
11
+ @connection = connection
12
+ @ids = ids
13
+ @retry_options = retry_options
14
+ end
15
+
16
+ def insert_and_return_count_of_rows_created
17
+ @connection.update(sql, should_retry: true, log_prefix: LOG_PREFIX)
18
+ end
19
+
20
+ def bottom
21
+ @ids[0]
22
+ end
23
+
24
+ def top
25
+ @ids[-1]
26
+ end
27
+
28
+ def expected_rows
29
+ @ids.length
30
+ end
31
+
32
+ private
33
+
34
+ def sql
35
+ "insert ignore into `#{ @migration.destination_name }` (#{ @migration.destination_columns }) " \
36
+ "select #{ @migration.origin_columns } from `#{ @migration.origin_name }` " \
37
+ "#{ conditions } `#{ @migration.origin_name }`.`id` in (#{@ids.join(',')})"
38
+ end
39
+
40
+ # XXX this is extremely brittle and doesn't work when filter contains more
41
+ # than one SQL clause, e.g. "where ... group by foo". Before making any
42
+ # more changes here, please consider either:
43
+ #
44
+ # 1. Letting users only specify part of defined clauses (i.e. don't allow
45
+ # `filter` on Migrator to accept both WHERE and INNER JOIN
46
+ # 2. Changing query building so that it uses structured data rather than
47
+ # strings until the last possible moment.
48
+ def conditions
49
+ if @migration.conditions
50
+ @migration.conditions.
51
+ # strip ending paren
52
+ sub(/\)\Z/, '').
53
+ # put any where conditions in parens
54
+ sub(/where\s(\w.*)\Z/, 'where (\\1)') + ' and'
55
+ else
56
+ 'where'
57
+ end
58
+ end
59
+ end
60
+ end
data/lib/lhm/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Schmidt
3
3
 
4
4
  module Lhm
5
- VERSION = '3.6.0'
5
+ VERSION = '3.6.1'
6
6
  end
@@ -0,0 +1,270 @@
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
+ require 'lhm/id_set_chunk_finder'
8
+
9
+ describe Lhm::IdSetChunkFinder do
10
+ include IntegrationHelper
11
+
12
+ before(:each) { connect_master! }
13
+
14
+ describe 'copying' do
15
+ before(:each) do
16
+ @origin = table_create(:origin)
17
+ @destination = table_create(:destination)
18
+ @migration = Lhm::Migration.new(@origin, @destination)
19
+ @logs = StringIO.new
20
+ Lhm.logger = Logger.new(@logs)
21
+ end
22
+
23
+ def log_messages
24
+ @logs.string.split("\n")
25
+ end
26
+
27
+ it 'should copy 1 row from origin to destination even if the id of the single row does not start at 1' do
28
+ execute("insert into origin set id = 1001 ")
29
+
30
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
31
+
32
+ slave do
33
+ value(count_all(@destination.name)).must_equal(1)
34
+ end
35
+
36
+ end
37
+
38
+ it 'should copy and ignore duplicate primary key' do
39
+ execute("insert into origin set id = 1001 ")
40
+ execute("insert into origin set id = 1002 ")
41
+ execute("insert into destination set id = 1002 ")
42
+
43
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
44
+
45
+ slave do
46
+ value(count_all(@destination.name)).must_equal(2)
47
+ end
48
+ end
49
+
50
+ it 'should copy and ignore duplicate composite primary key' do
51
+ origin = table_create(:composite_primary_key)
52
+ destination = table_create(:composite_primary_key_dest)
53
+ migration = Lhm::Migration.new(origin, destination)
54
+
55
+ execute("insert into composite_primary_key set id = 1001, shop_id = 1")
56
+ execute("insert into composite_primary_key set id = 1002, shop_id = 1")
57
+ execute("insert into composite_primary_key_dest set id = 1002, shop_id = 1")
58
+
59
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
60
+
61
+ slave do
62
+ value(count_all(destination.name)).must_equal(2)
63
+ end
64
+ end
65
+
66
+ it 'should copy and raise on unexpected warnings' do
67
+ origin = table_create(:custom_primary_key)
68
+ destination = table_create(:custom_primary_key_dest)
69
+ migration = Lhm::Migration.new(origin, destination)
70
+
71
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
72
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
73
+
74
+ exception = assert_raises(Lhm::Error) do
75
+ Lhm::Chunker.new(migration, connection, {raise_on_warnings: true, throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
76
+ end
77
+
78
+ assert_match "Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'", exception.message
79
+ end
80
+
81
+ it 'should copy and warn on unexpected warnings by default' do
82
+ origin = table_create(:custom_primary_key)
83
+ destination = table_create(:custom_primary_key_dest)
84
+ migration = Lhm::Migration.new(origin, destination)
85
+
86
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
87
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
88
+
89
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
90
+
91
+ assert_equal 2, log_messages.length
92
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
93
+ end
94
+
95
+ it 'should log two times for two unexpected warnings' do
96
+ origin = table_create(:custom_primary_key)
97
+ destination = table_create(:custom_primary_key_dest)
98
+ migration = Lhm::Migration.new(origin, destination)
99
+
100
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
101
+ execute("insert into custom_primary_key set id = 1002, pk = 2")
102
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 3")
103
+ execute("insert into custom_primary_key_dest set id = 1002, pk = 4")
104
+
105
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
106
+
107
+ assert_equal 3, log_messages.length
108
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
109
+ assert log_messages[2].include?("Unexpected warning found for inserted row: Duplicate entry '1002' for key 'index_custom_primary_key_on_id'"), log_messages
110
+ end
111
+
112
+ it 'should copy and warn on unexpected warnings' do
113
+ origin = table_create(:custom_primary_key)
114
+ destination = table_create(:custom_primary_key_dest)
115
+ migration = Lhm::Migration.new(origin, destination)
116
+
117
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
118
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
119
+
120
+ Lhm::Chunker.new(migration, connection, {raise_on_warnings: false, throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
121
+
122
+ assert_equal 2, log_messages.length
123
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
124
+ end
125
+
126
+ it 'should create the modified destination, even if the source is empty' do
127
+ execute("truncate origin ")
128
+
129
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder} ).run
130
+
131
+ slave do
132
+ value(count_all(@destination.name)).must_equal(0)
133
+ end
134
+
135
+ end
136
+
137
+ it 'should copy 23 rows from origin to destination in one shot, regardless of the value of the id' do
138
+ 23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
139
+
140
+ printer = MiniTest::Mock.new
141
+ printer.expect(:notify, :return_value, [Integer, Integer])
142
+ printer.expect(:end, :return_value, [])
143
+
144
+ Lhm::Chunker.new(
145
+ @migration, connection, { throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder }
146
+ ).run
147
+
148
+ slave do
149
+ value(count_all(@destination.name)).must_equal(23)
150
+ end
151
+
152
+ printer.verify
153
+
154
+ end
155
+
156
+ it 'should copy all the records of a table, even if the last chunk starts with the last record of it.' do
157
+ 11.times { |n| execute("insert into origin set id = '#{ n + 1 }'") }
158
+
159
+
160
+ Lhm::Chunker.new(
161
+ @migration, connection, { throttler: Lhm::Throttler::Time.new(stride: 10), printer: printer, chunk_finder: Lhm::IdSetChunkFinder }
162
+ ).run
163
+
164
+ slave do
165
+ value(count_all(@destination.name)).must_equal(11)
166
+ end
167
+
168
+ end
169
+
170
+ 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
171
+ 23.times { |n| execute("insert into origin set id = '#{ 100000 + n * n + 23 }'") }
172
+
173
+ printer = MiniTest::Mock.new
174
+ printer.expect(:notify, :return_value, [Integer, Integer])
175
+ printer.expect(:end, :return_value, [])
176
+
177
+ Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
178
+ Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
179
+
180
+ Lhm::Chunker.new(
181
+ @migration, connection, { throttler: Lhm::Throttler::SlaveLag.new(stride: 100), printer: printer, chunk_finder: Lhm::IdSetChunkFinder }
182
+ ).run
183
+
184
+ slave do
185
+ value(count_all(@destination.name)).must_equal(23)
186
+ end
187
+
188
+ printer.verify
189
+ end
190
+
191
+ it 'should throttle work stride based on slave lag' do
192
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
193
+
194
+ printer = mock()
195
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
196
+ printer.expects(:end)
197
+
198
+ throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0)
199
+ def throttler.max_current_slave_lag
200
+ 1
201
+ end
202
+
203
+ Lhm::Chunker.new(
204
+ @migration, connection, { throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder }
205
+ ).run
206
+
207
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
208
+
209
+ slave do
210
+ value(count_all(@destination.name)).must_equal(15)
211
+ end
212
+ end
213
+
214
+ it 'should detect a single slave with no lag in the default configuration' do
215
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
216
+
217
+ printer = mock()
218
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
219
+ printer.expects(:verify)
220
+ printer.expects(:end)
221
+
222
+ Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
223
+ Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
224
+
225
+ throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0)
226
+
227
+ if master_slave_mode?
228
+ def throttler.slave_connection(slave)
229
+ config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup
230
+ config[:host] = slave
231
+ config[:port] = 33007
232
+ ActiveRecord::Base.send('mysql2_connection', config)
233
+ end
234
+ end
235
+
236
+ Lhm::Chunker.new(
237
+ @migration, connection, { throttler: throttler, printer: printer, chunk_finder: Lhm::IdSetChunkFinder }
238
+ ).run
239
+
240
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT, throttler.timeout_seconds)
241
+ assert_equal(0, throttler.send(:max_current_slave_lag))
242
+
243
+ slave do
244
+ value(count_all(@destination.name)).must_equal(15)
245
+ end
246
+
247
+ printer.verify
248
+ end
249
+
250
+ it 'should abort early if the triggers are removed' do
251
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
252
+
253
+ printer = mock()
254
+
255
+ failer = Proc.new { false }
256
+
257
+ exception = assert_raises do
258
+ Lhm::Chunker.new(
259
+ @migration, connection, { verifier: failer, printer: printer, throttler: throttler, chunk_finder: Lhm::IdSetChunkFinder }
260
+ ).run
261
+ end
262
+
263
+ assert_match "Verification failed, aborting early", exception.message
264
+
265
+ slave do
266
+ value(count_all(@destination.name)).must_equal(0)
267
+ end
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
2
+ require 'lhm/migration'
3
+ require 'lhm/id_set_chunk_insert'
4
+
5
+ describe Lhm::IdSetChunkInsert do
6
+ include IntegrationHelper
7
+
8
+ describe 'insert_and_return_count_of_rows_created' do
9
+ before(:each) do
10
+ connect_master!
11
+ @origin = table_create(:origin)
12
+ @destination = table_create(:destination)
13
+ @migration = Lhm::Migration.new(@origin, @destination)
14
+ execute("insert into origin set id = 1001")
15
+ @connection = Lhm::Connection.new(connection: connection)
16
+ @instance = Lhm::IdSetChunkInsert.new(@migration, @connection, [1001])
17
+ end
18
+
19
+ it "returns the count" do
20
+ assert_equal 1, @instance.insert_and_return_count_of_rows_created
21
+ end
22
+
23
+ it "inserts the record into the slave" do
24
+ @instance.insert_and_return_count_of_rows_created
25
+
26
+ slave do
27
+ value(count_all(@destination.name)).must_equal(1)
28
+ end
29
+ end
30
+ end
31
+ end
data/spec/test_helper.rb CHANGED
@@ -3,7 +3,9 @@
3
3
 
4
4
  if ENV['COV']
5
5
  require 'simplecov'
6
- SimpleCov.start
6
+ SimpleCov.start do
7
+ enable_coverage :branch
8
+ end
7
9
  end
8
10
 
9
11
  require 'minitest/autorun'
@@ -65,5 +67,3 @@ def init_test_db
65
67
  end
66
68
 
67
69
  init_test_db
68
-
69
-
@@ -0,0 +1,145 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/chunker'
9
+ require 'lhm/throttler'
10
+ require 'lhm/connection'
11
+ require 'lhm/id_set_chunk_finder'
12
+
13
+ describe Lhm::IdSetChunkFinder do
14
+ include UnitHelper
15
+
16
+ EXPECTED_RETRY_FLAGS_CHUNKER = {:should_retry => true, :log_prefix => "Chunker"}
17
+ EXPECTED_RETRY_FLAGS_CHUNK_INSERT = {:should_retry => true, :log_prefix => "ChunkInsert"}
18
+
19
+ before(:each) do
20
+ @origin = Lhm::Table.new('foo')
21
+ @destination = Lhm::Table.new('bar')
22
+ @migration = Lhm::Migration.new(@origin, @destination)
23
+ @connection = mock()
24
+ @connection.stubs(:execute).returns([["dummy"]])
25
+ # This is a poor man's stub
26
+ @throttler = Object.new
27
+ def @throttler.run
28
+ # noop
29
+ end
30
+ def @throttler.stride
31
+ 1
32
+ end
33
+
34
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
35
+ :chunk_finder => Lhm::IdSetChunkFinder)
36
+ end
37
+
38
+ describe '#run' do
39
+ it 'chunks the result set according to the stride size' do
40
+ def @throttler.stride
41
+ 2
42
+ end
43
+
44
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns((1..20).select(&:odd?))
45
+
46
+ @connection.expects(:update).with(regexp_matches(/`id` in \(1,3\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
47
+ @connection.expects(:update).with(regexp_matches(/`id` in \(5,7\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
48
+ @connection.expects(:update).with(regexp_matches(/`id` in \(9,11\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
49
+ @connection.expects(:update).with(regexp_matches(/`id` in \(13,15\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
50
+ @connection.expects(:update).with(regexp_matches(/`id` in \(17,19\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
51
+
52
+ @chunker.run
53
+ end
54
+
55
+ it 'handles stride changes during execution' do
56
+ # roll our own stubbing
57
+ def @throttler.stride
58
+ @run_count ||= 0
59
+ @run_count = @run_count + 1
60
+ if @run_count > 1
61
+ 3
62
+ else
63
+ 2
64
+ end
65
+ end
66
+
67
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns((1..20).select(&:odd?))
68
+
69
+ @connection.expects(:update).with(regexp_matches(/`id` in \(1,3\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
70
+ @connection.expects(:update).with(regexp_matches(/`id` in \(5,7,9\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
71
+ @connection.expects(:update).with(regexp_matches(/`id` in \(11,13,15\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
72
+ @connection.expects(:update).with(regexp_matches(/`id` in \(17,19\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
73
+
74
+ @connection.expects(:execute).twice.with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
75
+
76
+ @chunker.run
77
+ end
78
+
79
+ it 'correctly copies single record tables' do
80
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
81
+ :chunk_finder => Lhm::IdSetChunkFinder)
82
+
83
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([1])
84
+ @connection.expects(:update).with(regexp_matches(/`id` in \(1\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
85
+
86
+ @chunker.run
87
+ end
88
+
89
+ it 'copies the last record of a table, even it is the start of the last chunk' do
90
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
91
+ :chunk_finder => Lhm::IdSetChunkFinder)
92
+ def @throttler.stride
93
+ 2
94
+ end
95
+
96
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns((2..10).to_a)
97
+
98
+ @connection.expects(:update).with(regexp_matches(/`id` in \(2,3\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
99
+ @connection.expects(:update).with(regexp_matches(/`id` in \(4,5\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
100
+ @connection.expects(:update).with(regexp_matches(/`id` in \(6,7\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
101
+ @connection.expects(:update).with(regexp_matches(/`id` in \(8,9\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2)
102
+ @connection.expects(:update).with(regexp_matches(/`id` in \(10\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
103
+
104
+ @chunker.run
105
+ end
106
+
107
+
108
+ it 'separates filter conditions from chunking conditions' do
109
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
110
+ :chunk_finder => Lhm::IdSetChunkFinder)
111
+ def @throttler.stride
112
+ 2
113
+ end
114
+
115
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([1, 2])
116
+ @connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`.*`id` in \(1,2\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
117
+ @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
118
+
119
+ def @migration.conditions
120
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
121
+ end
122
+
123
+ @chunker.run
124
+ end
125
+
126
+ it "doesn't mess with inner join filters" do
127
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
128
+ :chunk_finder => Lhm::IdSetChunkFinder)
129
+
130
+ def @throttler.stride
131
+ 2
132
+ end
133
+
134
+ @connection.expects(:select_values).with(regexp_matches(/order by id asc/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([1,2])
135
+ @connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and.*`id` in \(1,2\)/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1)
136
+ @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([])
137
+
138
+ def @migration.conditions
139
+ 'inner join bar on foo.id = bar.foo_id'
140
+ end
141
+
142
+ @chunker.run
143
+ end
144
+ end
145
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhm-teak
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - SoundCloud
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2023-06-06 00:00:00.000000000 Z
16
+ date: 2023-06-07 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: retriable
@@ -209,6 +209,8 @@ files:
209
209
  - lib/lhm/command.rb
210
210
  - lib/lhm/connection.rb
211
211
  - lib/lhm/entangler.rb
212
+ - lib/lhm/id_set_chunk_finder.rb
213
+ - lib/lhm/id_set_chunk_insert.rb
212
214
  - lib/lhm/intersection.rb
213
215
  - lib/lhm/invoker.rb
214
216
  - lib/lhm/locked_switcher.rb
@@ -255,6 +257,8 @@ files:
255
257
  - spec/integration/cleanup_spec.rb
256
258
  - spec/integration/database.yml
257
259
  - spec/integration/entangler_spec.rb
260
+ - spec/integration/id_set_chunk_finder_spec.rb
261
+ - spec/integration/id_set_chunk_insert_spec.rb
258
262
  - spec/integration/integration_helper.rb
259
263
  - spec/integration/invoker_spec.rb
260
264
  - spec/integration/lhm_spec.rb
@@ -275,6 +279,7 @@ files:
275
279
  - spec/unit/chunker_spec.rb
276
280
  - spec/unit/connection_spec.rb
277
281
  - spec/unit/entangler_spec.rb
282
+ - spec/unit/id_set_chunk_finder_spec.rb
278
283
  - spec/unit/intersection_spec.rb
279
284
  - spec/unit/lhm_spec.rb
280
285
  - spec/unit/locked_switcher_spec.rb
@@ -332,6 +337,8 @@ test_files:
332
337
  - spec/integration/cleanup_spec.rb
333
338
  - spec/integration/database.yml
334
339
  - spec/integration/entangler_spec.rb
340
+ - spec/integration/id_set_chunk_finder_spec.rb
341
+ - spec/integration/id_set_chunk_insert_spec.rb
335
342
  - spec/integration/integration_helper.rb
336
343
  - spec/integration/invoker_spec.rb
337
344
  - spec/integration/lhm_spec.rb
@@ -352,6 +359,7 @@ test_files:
352
359
  - spec/unit/chunker_spec.rb
353
360
  - spec/unit/connection_spec.rb
354
361
  - spec/unit/entangler_spec.rb
362
+ - spec/unit/id_set_chunk_finder_spec.rb
355
363
  - spec/unit/intersection_spec.rb
356
364
  - spec/unit/lhm_spec.rb
357
365
  - spec/unit/locked_switcher_spec.rb