lhm-teak 3.6.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.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +43 -0
  3. data/.gitignore +12 -0
  4. data/.rubocop.yml +183 -0
  5. data/.travis.yml +21 -0
  6. data/Appraisals +24 -0
  7. data/CHANGELOG.md +254 -0
  8. data/Gemfile +5 -0
  9. data/Gemfile.lock +67 -0
  10. data/LICENSE +27 -0
  11. data/README.md +335 -0
  12. data/Rakefile +33 -0
  13. data/dev.yml +45 -0
  14. data/docker-compose.yml +60 -0
  15. data/gemfiles/activerecord_5.2.gemfile +9 -0
  16. data/gemfiles/activerecord_5.2.gemfile.lock +66 -0
  17. data/gemfiles/activerecord_6.0.gemfile +7 -0
  18. data/gemfiles/activerecord_6.0.gemfile.lock +68 -0
  19. data/gemfiles/activerecord_6.1.gemfile +7 -0
  20. data/gemfiles/activerecord_6.1.gemfile.lock +67 -0
  21. data/gemfiles/activerecord_7.0.0.alpha2.gemfile +7 -0
  22. data/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +65 -0
  23. data/lhm.gemspec +38 -0
  24. data/lib/lhm/atomic_switcher.rb +46 -0
  25. data/lib/lhm/chunk_finder.rb +62 -0
  26. data/lib/lhm/chunk_insert.rb +61 -0
  27. data/lib/lhm/chunker.rb +95 -0
  28. data/lib/lhm/cleanup/current.rb +71 -0
  29. data/lib/lhm/command.rb +48 -0
  30. data/lib/lhm/connection.rb +108 -0
  31. data/lib/lhm/entangler.rb +112 -0
  32. data/lib/lhm/intersection.rb +51 -0
  33. data/lib/lhm/invoker.rb +100 -0
  34. data/lib/lhm/locked_switcher.rb +76 -0
  35. data/lib/lhm/migration.rb +51 -0
  36. data/lib/lhm/migrator.rb +244 -0
  37. data/lib/lhm/printer.rb +63 -0
  38. data/lib/lhm/proxysql_helper.rb +10 -0
  39. data/lib/lhm/railtie.rb +9 -0
  40. data/lib/lhm/sql_helper.rb +77 -0
  41. data/lib/lhm/sql_retry.rb +180 -0
  42. data/lib/lhm/table.rb +121 -0
  43. data/lib/lhm/table_name.rb +23 -0
  44. data/lib/lhm/test_support.rb +35 -0
  45. data/lib/lhm/throttler/slave_lag.rb +162 -0
  46. data/lib/lhm/throttler/threads_running.rb +53 -0
  47. data/lib/lhm/throttler/time.rb +29 -0
  48. data/lib/lhm/throttler.rb +36 -0
  49. data/lib/lhm/timestamp.rb +11 -0
  50. data/lib/lhm/version.rb +6 -0
  51. data/lib/lhm-shopify.rb +1 -0
  52. data/lib/lhm.rb +156 -0
  53. data/scripts/helpers/wait-for-dbs.sh +21 -0
  54. data/scripts/mysql/reader/create_replication.sql +10 -0
  55. data/scripts/mysql/writer/create_test_db.sql +1 -0
  56. data/scripts/mysql/writer/create_users.sql +6 -0
  57. data/scripts/proxysql/proxysql.cnf +117 -0
  58. data/shipit.rubygems.yml +0 -0
  59. data/spec/.lhm.example +4 -0
  60. data/spec/README.md +58 -0
  61. data/spec/fixtures/bigint_table.ddl +4 -0
  62. data/spec/fixtures/composite_primary_key.ddl +6 -0
  63. data/spec/fixtures/composite_primary_key_dest.ddl +6 -0
  64. data/spec/fixtures/custom_primary_key.ddl +6 -0
  65. data/spec/fixtures/custom_primary_key_dest.ddl +6 -0
  66. data/spec/fixtures/destination.ddl +6 -0
  67. data/spec/fixtures/lines.ddl +7 -0
  68. data/spec/fixtures/origin.ddl +6 -0
  69. data/spec/fixtures/permissions.ddl +5 -0
  70. data/spec/fixtures/small_table.ddl +4 -0
  71. data/spec/fixtures/tracks.ddl +5 -0
  72. data/spec/fixtures/users.ddl +14 -0
  73. data/spec/fixtures/wo_id_int_column.ddl +6 -0
  74. data/spec/integration/atomic_switcher_spec.rb +129 -0
  75. data/spec/integration/chunk_insert_spec.rb +30 -0
  76. data/spec/integration/chunker_spec.rb +269 -0
  77. data/spec/integration/cleanup_spec.rb +147 -0
  78. data/spec/integration/database.yml +25 -0
  79. data/spec/integration/entangler_spec.rb +68 -0
  80. data/spec/integration/integration_helper.rb +252 -0
  81. data/spec/integration/invoker_spec.rb +33 -0
  82. data/spec/integration/lhm_spec.rb +659 -0
  83. data/spec/integration/lock_wait_timeout_spec.rb +30 -0
  84. data/spec/integration/locked_switcher_spec.rb +50 -0
  85. data/spec/integration/proxysql_spec.rb +34 -0
  86. data/spec/integration/sql_retry/db_connection_helper.rb +52 -0
  87. data/spec/integration/sql_retry/lock_wait_spec.rb +127 -0
  88. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +114 -0
  89. data/spec/integration/sql_retry/proxysql_helper.rb +22 -0
  90. data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +109 -0
  91. data/spec/integration/table_spec.rb +83 -0
  92. data/spec/integration/toxiproxy_helper.rb +40 -0
  93. data/spec/test_helper.rb +69 -0
  94. data/spec/unit/atomic_switcher_spec.rb +29 -0
  95. data/spec/unit/chunk_finder_spec.rb +73 -0
  96. data/spec/unit/chunk_insert_spec.rb +67 -0
  97. data/spec/unit/chunker_spec.rb +176 -0
  98. data/spec/unit/connection_spec.rb +111 -0
  99. data/spec/unit/entangler_spec.rb +187 -0
  100. data/spec/unit/intersection_spec.rb +51 -0
  101. data/spec/unit/lhm_spec.rb +46 -0
  102. data/spec/unit/locked_switcher_spec.rb +46 -0
  103. data/spec/unit/migrator_spec.rb +144 -0
  104. data/spec/unit/printer_spec.rb +85 -0
  105. data/spec/unit/sql_helper_spec.rb +28 -0
  106. data/spec/unit/table_name_spec.rb +39 -0
  107. data/spec/unit/table_spec.rb +47 -0
  108. data/spec/unit/throttler/slave_lag_spec.rb +322 -0
  109. data/spec/unit/throttler/threads_running_spec.rb +64 -0
  110. data/spec/unit/throttler_spec.rb +124 -0
  111. data/spec/unit/unit_helper.rb +26 -0
  112. metadata +366 -0
@@ -0,0 +1,30 @@
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
+ @connection = Lhm::Connection.new(connection: connection)
15
+ @instance = Lhm::ChunkInsert.new(@migration, @connection, 1001, 1001)
16
+ end
17
+
18
+ it "returns the count" do
19
+ assert_equal 1, @instance.insert_and_return_count_of_rows_created
20
+ end
21
+
22
+ it "inserts the record into the slave" do
23
+ @instance.insert_and_return_count_of_rows_created
24
+
25
+ slave do
26
+ value(count_all(@destination.name)).must_equal(1)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,269 @@
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
+ @logs = StringIO.new
19
+ Lhm.logger = Logger.new(@logs)
20
+ end
21
+
22
+ def log_messages
23
+ @logs.string.split("\n")
24
+ end
25
+
26
+ it 'should copy 1 row from origin to destination even if the id of the single row does not start at 1' do
27
+ execute("insert into origin set id = 1001 ")
28
+
29
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
30
+
31
+ slave do
32
+ value(count_all(@destination.name)).must_equal(1)
33
+ end
34
+
35
+ end
36
+
37
+ it 'should copy and ignore duplicate primary key' do
38
+ execute("insert into origin set id = 1001 ")
39
+ execute("insert into origin set id = 1002 ")
40
+ execute("insert into destination set id = 1002 ")
41
+
42
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
43
+
44
+ slave do
45
+ value(count_all(@destination.name)).must_equal(2)
46
+ end
47
+ end
48
+
49
+ it 'should copy and ignore duplicate composite primary key' do
50
+ origin = table_create(:composite_primary_key)
51
+ destination = table_create(:composite_primary_key_dest)
52
+ migration = Lhm::Migration.new(origin, destination)
53
+
54
+ execute("insert into composite_primary_key set id = 1001, shop_id = 1")
55
+ execute("insert into composite_primary_key set id = 1002, shop_id = 1")
56
+ execute("insert into composite_primary_key_dest set id = 1002, shop_id = 1")
57
+
58
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
59
+
60
+ slave do
61
+ value(count_all(destination.name)).must_equal(2)
62
+ end
63
+ end
64
+
65
+ it 'should copy and raise on unexpected warnings' do
66
+ origin = table_create(:custom_primary_key)
67
+ destination = table_create(:custom_primary_key_dest)
68
+ migration = Lhm::Migration.new(origin, destination)
69
+
70
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
71
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
72
+
73
+ exception = assert_raises(Lhm::Error) do
74
+ Lhm::Chunker.new(migration, connection, {raise_on_warnings: true, throttler: throttler, printer: printer} ).run
75
+ end
76
+
77
+ assert_match "Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'", exception.message
78
+ end
79
+
80
+ it 'should copy and warn on unexpected warnings by default' do
81
+ origin = table_create(:custom_primary_key)
82
+ destination = table_create(:custom_primary_key_dest)
83
+ migration = Lhm::Migration.new(origin, destination)
84
+
85
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
86
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
87
+
88
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
89
+
90
+ assert_equal 2, log_messages.length
91
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
92
+ end
93
+
94
+ it 'should log two times for two unexpected warnings' do
95
+ origin = table_create(:custom_primary_key)
96
+ destination = table_create(:custom_primary_key_dest)
97
+ migration = Lhm::Migration.new(origin, destination)
98
+
99
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
100
+ execute("insert into custom_primary_key set id = 1002, pk = 2")
101
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 3")
102
+ execute("insert into custom_primary_key_dest set id = 1002, pk = 4")
103
+
104
+ Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
105
+
106
+ assert_equal 3, log_messages.length
107
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
108
+ assert log_messages[2].include?("Unexpected warning found for inserted row: Duplicate entry '1002' for key 'index_custom_primary_key_on_id'"), log_messages
109
+ end
110
+
111
+ it 'should copy and warn on unexpected warnings' do
112
+ origin = table_create(:custom_primary_key)
113
+ destination = table_create(:custom_primary_key_dest)
114
+ migration = Lhm::Migration.new(origin, destination)
115
+
116
+ execute("insert into custom_primary_key set id = 1001, pk = 1")
117
+ execute("insert into custom_primary_key_dest set id = 1001, pk = 2")
118
+
119
+ Lhm::Chunker.new(migration, connection, {raise_on_warnings: false, throttler: throttler, printer: printer} ).run
120
+
121
+ assert_equal 2, log_messages.length
122
+ assert log_messages[1].include?("Unexpected warning found for inserted row: Duplicate entry '1001' for key 'index_custom_primary_key_on_id'"), log_messages
123
+ end
124
+
125
+ it 'should create the modified destination, even if the source is empty' do
126
+ execute("truncate origin ")
127
+
128
+ Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
129
+
130
+ slave do
131
+ value(count_all(@destination.name)).must_equal(0)
132
+ end
133
+
134
+ end
135
+
136
+ it 'should copy 23 rows from origin to destination in one shot, regardless of the value of the id' do
137
+ 23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
138
+
139
+ printer = MiniTest::Mock.new
140
+ printer.expect(:notify, :return_value, [Integer, Integer])
141
+ printer.expect(:end, :return_value, [])
142
+
143
+ Lhm::Chunker.new(
144
+ @migration, connection, { throttler: throttler, printer: printer }
145
+ ).run
146
+
147
+ slave do
148
+ value(count_all(@destination.name)).must_equal(23)
149
+ end
150
+
151
+ printer.verify
152
+
153
+ end
154
+
155
+ it 'should copy all the records of a table, even if the last chunk starts with the last record of it.' do
156
+ 11.times { |n| execute("insert into origin set id = '#{ n + 1 }'") }
157
+
158
+
159
+ Lhm::Chunker.new(
160
+ @migration, connection, { throttler: Lhm::Throttler::Time.new(stride: 10), printer: printer }
161
+ ).run
162
+
163
+ slave do
164
+ value(count_all(@destination.name)).must_equal(11)
165
+ end
166
+
167
+ end
168
+
169
+ 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
170
+ 23.times { |n| execute("insert into origin set id = '#{ 100000 + n * n + 23 }'") }
171
+
172
+ printer = MiniTest::Mock.new
173
+ printer.expect(:notify, :return_value, [Integer, Integer])
174
+ printer.expect(:end, :return_value, [])
175
+
176
+ Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
177
+ Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
178
+
179
+ Lhm::Chunker.new(
180
+ @migration, connection, { throttler: Lhm::Throttler::SlaveLag.new(stride: 100), printer: printer }
181
+ ).run
182
+
183
+ slave do
184
+ value(count_all(@destination.name)).must_equal(23)
185
+ end
186
+
187
+ printer.verify
188
+ end
189
+
190
+ it 'should throttle work stride based on slave lag' do
191
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
192
+
193
+ printer = mock()
194
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
195
+ printer.expects(:end)
196
+
197
+ throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0)
198
+ def throttler.max_current_slave_lag
199
+ 1
200
+ end
201
+
202
+ Lhm::Chunker.new(
203
+ @migration, connection, { throttler: throttler, printer: printer }
204
+ ).run
205
+
206
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
207
+
208
+ slave do
209
+ value(count_all(@destination.name)).must_equal(15)
210
+ end
211
+ end
212
+
213
+ it 'should detect a single slave with no lag in the default configuration' do
214
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
215
+
216
+ printer = mock()
217
+ printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
218
+ printer.expects(:verify)
219
+ printer.expects(:end)
220
+
221
+ Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1'])
222
+ Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1'])
223
+
224
+ throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0)
225
+
226
+ if master_slave_mode?
227
+ def throttler.slave_connection(slave)
228
+ config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup
229
+ config[:host] = slave
230
+ config[:port] = 33007
231
+ ActiveRecord::Base.send('mysql2_connection', config)
232
+ end
233
+ end
234
+
235
+ Lhm::Chunker.new(
236
+ @migration, connection, { throttler: throttler, printer: printer }
237
+ ).run
238
+
239
+ assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT, throttler.timeout_seconds)
240
+ assert_equal(0, throttler.send(:max_current_slave_lag))
241
+
242
+ slave do
243
+ value(count_all(@destination.name)).must_equal(15)
244
+ end
245
+
246
+ printer.verify
247
+ end
248
+
249
+ it 'should abort early if the triggers are removed' do
250
+ 15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
251
+
252
+ printer = mock()
253
+
254
+ failer = Proc.new { false }
255
+
256
+ exception = assert_raises do
257
+ Lhm::Chunker.new(
258
+ @migration, connection, { verifier: failer, printer: printer, throttler: throttler }
259
+ ).run
260
+ end
261
+
262
+ assert_match "Verification failed, aborting early", exception.message
263
+
264
+ slave do
265
+ value(count_all(@destination.name)).must_equal(0)
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,147 @@
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
+ describe Lhm, 'cleanup' do
7
+ include IntegrationHelper
8
+ before(:each) { connect_master! }
9
+
10
+ describe 'changes' do
11
+ before(:each) do
12
+ table_create(:users)
13
+ table_create(:permissions)
14
+ simulate_failed_migration do
15
+ Lhm.change_table(:users, :atomic_switch => false) do |t|
16
+ t.add_column(:logins, "INT(12) DEFAULT '0'")
17
+ t.add_index(:logins)
18
+ end
19
+ end
20
+ simulate_failed_migration do
21
+ Lhm.change_table(:permissions, :atomic_switch => false) do |t|
22
+ t.add_column(:user_id, "INT(12) DEFAULT '0'")
23
+ t.add_index(:user_id)
24
+ end
25
+ end
26
+ end
27
+
28
+ after(:each) do
29
+ Lhm.cleanup(true)
30
+ end
31
+
32
+ describe 'cleanup' do
33
+ it 'should show temporary tables' do
34
+ output = capture_stdout do |logger|
35
+ Lhm.logger = logger
36
+ Lhm.cleanup
37
+ end
38
+ value(output).must_include('Would drop LHM backup tables')
39
+ value(output).must_match(/lhma_[0-9_]*_users/)
40
+ value(output).must_match(/lhma_[0-9_]*_permissions/)
41
+ end
42
+
43
+ it 'should show temporary tables within range' do
44
+ table = OpenStruct.new(:name => 'users')
45
+ table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now - 172800).archive_name
46
+ table_rename(:users, table_name)
47
+
48
+ table2 = OpenStruct.new(:name => 'permissions')
49
+ table_name2 = Lhm::Migration.new(table2, nil, nil, {}, Time.now - 172800).archive_name
50
+ table_rename(:permissions, table_name2)
51
+
52
+ output = capture_stdout do |logger|
53
+ Lhm.logger = logger
54
+ Lhm.cleanup false, { :until => Time.now - 86400 }
55
+ end
56
+ value(output).must_include('Would drop LHM backup tables')
57
+ value(output).must_match(/lhma_[0-9_]*_users/)
58
+ value(output).must_match(/lhma_[0-9_]*_permissions/)
59
+ end
60
+
61
+ it 'should exclude temporary tables outside range' do
62
+ table = OpenStruct.new(:name => 'users')
63
+ table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now).archive_name
64
+ table_rename(:users, table_name)
65
+
66
+ table2 = OpenStruct.new(:name => 'permissions')
67
+ table_name2 = Lhm::Migration.new(table2, nil, nil, {}, Time.now).archive_name
68
+ table_rename(:permissions, table_name2)
69
+
70
+ output = capture_stdout do |logger|
71
+ Lhm.logger = logger
72
+ Lhm.cleanup false, { :until => Time.now - 172800 }
73
+ end
74
+ value(output).must_include('Would drop LHM backup tables')
75
+ value(output).wont_match(/lhma_[0-9_]*_users/)
76
+ value(output).wont_match(/lhma_[0-9_]*_permissions/)
77
+ end
78
+
79
+ it 'should show temporary triggers' do
80
+ output = capture_stdout do |logger|
81
+ Lhm.logger = logger
82
+ Lhm.cleanup
83
+ end
84
+ value(output).must_include('Would drop LHM triggers')
85
+ value(output).must_include('lhmt_ins_users')
86
+ value(output).must_include('lhmt_del_users')
87
+ value(output).must_include('lhmt_upd_users')
88
+ value(output).must_include('lhmt_ins_permissions')
89
+ value(output).must_include('lhmt_del_permissions')
90
+ value(output).must_include('lhmt_upd_permissions')
91
+ end
92
+
93
+ it 'should delete temporary tables' do
94
+ value(Lhm.cleanup(true)).must_equal(true)
95
+ value(Lhm.cleanup).must_equal(true)
96
+ end
97
+
98
+ it 'outputs deleted tables and triggers' do
99
+ output = capture_stdout do |logger|
100
+ Lhm.logger = logger
101
+ Lhm.cleanup(true)
102
+ end
103
+ value(output).must_include('Dropped triggers lhmt_ins_users, lhmt_upd_users, lhmt_del_users, lhmt_ins_permissions, lhmt_upd_permissions, lhmt_del_permissions')
104
+ end
105
+ end
106
+
107
+ describe 'cleanup_current_run' do
108
+ it 'should show lhmn table for the specified table only' do
109
+ table_create(:permissions)
110
+ table_rename(:permissions, 'lhmn_permissions')
111
+ output = capture_stdout do |logger|
112
+ Lhm.logger = logger
113
+ Lhm.cleanup_current_run(false, 'permissions')
114
+ end
115
+
116
+ value(output).must_include("The following DDLs would be executed:")
117
+ value(output).must_include("drop trigger if exists lhmt_ins_permissions")
118
+ value(output).must_include("drop trigger if exists lhmt_upd_permissions")
119
+ value(output).must_include("drop trigger if exists lhmt_del_permissions")
120
+ end
121
+
122
+ it 'should show temporary triggers for the specified table only' do
123
+ output = capture_stdout do |logger|
124
+ Lhm.logger = logger
125
+ Lhm.cleanup_current_run(false, 'permissions')
126
+ end
127
+ value(output).must_include("The following DDLs would be executed:")
128
+ value(output).must_include("drop trigger if exists lhmt_ins_permissions")
129
+ value(output).must_include("drop trigger if exists lhmt_upd_permissions")
130
+ value(output).must_include("drop trigger if exists lhmt_del_permissions")
131
+ end
132
+
133
+ it 'should delete temporary tables and triggers for the specified table only' do
134
+ assert Lhm.cleanup_current_run(true, 'permissions')
135
+
136
+ all_tables = Lhm.connection.select_values('show tables')
137
+ all_triggers = Lhm.connection.select_values('show triggers')
138
+
139
+ refute all_tables.include?('lhmn_permissions')
140
+ assert all_tables.find { |t| t =~ /lhma_(.*)_users/}
141
+
142
+ refute all_triggers.find { |t| t =~ /lhmt_(.*)_permissions/}
143
+ assert all_triggers.find { |t| t =~ /lhmt_(.*)_users/}
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,25 @@
1
+ master:
2
+ host: mysql-1
3
+ user: root
4
+ password: password
5
+ port: 33006
6
+ slave:
7
+ host: mysql-2
8
+ user: root
9
+ password: password
10
+ port: 33007
11
+ proxysql:
12
+ host: proxysql
13
+ user: root
14
+ password: password
15
+ port: 33005
16
+ master_toxic:
17
+ host: toxiproxy
18
+ user: root
19
+ password: password
20
+ port: 22220
21
+ proxysql_toxic:
22
+ host: toxiproxy
23
+ user: root
24
+ password: password
25
+ port: 22222
@@ -0,0 +1,68 @@
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/entangler'
9
+ require 'lhm/connection'
10
+
11
+ describe Lhm::Entangler do
12
+ include IntegrationHelper
13
+
14
+ before(:each) { connect_master! }
15
+
16
+ describe 'entanglement' do
17
+ before(:each) do
18
+ @origin = table_create('origin')
19
+ @destination = table_create('destination')
20
+ @migration = Lhm::Migration.new(@origin, @destination)
21
+ @connection = Lhm::Connection.new(connection: connection)
22
+ @entangler = Lhm::Entangler.new(@migration, @connection)
23
+ end
24
+
25
+ it 'should replay inserts from origin into destination' do
26
+ @entangler.run do
27
+ execute("insert into origin (common) values ('inserted')")
28
+ end
29
+
30
+ slave do
31
+ value(count(:destination, 'common', 'inserted')).must_equal(1)
32
+ end
33
+ end
34
+
35
+ it 'should replay deletes from origin into destination' do
36
+ execute("insert into origin (common) values ('inserted')")
37
+
38
+ @entangler.run do
39
+ execute("delete from origin where common = 'inserted'")
40
+ end
41
+
42
+ slave do
43
+ value(count(:destination, 'common', 'inserted')).must_equal(0)
44
+ end
45
+ end
46
+
47
+ it 'should replay updates from origin into destination' do
48
+ @entangler.run do
49
+ execute("insert into origin (common) values ('inserted')")
50
+ execute("update origin set common = 'updated'")
51
+ end
52
+
53
+ slave do
54
+ value(count(:destination, 'common', 'updated')).must_equal(1)
55
+ end
56
+ end
57
+
58
+ it 'should remove entanglement' do
59
+ @entangler.run {}
60
+
61
+ execute("insert into origin (common) values ('inserted')")
62
+
63
+ slave do
64
+ value(count(:destination, 'common', 'inserted')).must_equal(0)
65
+ end
66
+ end
67
+ end
68
+ end