lhm-teak 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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