lhm-shopify 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c60de7019efd80463c07434cb2312197009347dc5e7b8d238c1f3ba0355b7a9e
4
- data.tar.gz: 44658f6f74b6c98739c6fd58046dd3ffa20436949f379eb9607a070afabeee8e
3
+ metadata.gz: ef0c285f7d63755816429aa2795220be251c0c93c4506f8255f7e547c074d574
4
+ data.tar.gz: 5de29d0b924375634c061a459837f0ea6a12d5ccf58226032ef25f708ef15628
5
5
  SHA512:
6
- metadata.gz: 318c3a3f051c8bfd8cb8e30bdfef2bad0c0c61fcbc7081780adb4eaba60023e94ac9d86b4f203a950ad31c304cc3bdd3affb88eb29633ef115185aab92596d55
7
- data.tar.gz: 644b728df70002076404e93133dbed4f53565bec70de02ca39640a482276ad7480de18a45a06a277bd55c1d6cad906026a7454bd05e7471efd055921da51708a
6
+ metadata.gz: 2d24b1d78c6fdd67129ee91a01d043ccefb1f2e8c4b8322553f1c6cbfed6237105e58eb6ddab0e143869b813682ea6b3c25e5a40e66199f76adc0cc99a97050b
7
+ data.tar.gz: 327b3a438ac2fb1b0763396094baed303a2ecb9a37558295b8b516b089bcca73534102351281883679263f7e69647d1129941df4970d3abae004faf93d91db62
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 3.4.1 (Sep 22, 2021)
2
+
3
+ * Add better logging to the LHM components (https://github.com/Shopify/lhm/pull/108)
4
+
1
5
  # 3.4.0 (Jul 19, 2021)
2
6
 
3
7
  * Log or raise on unexpected duplicated entry warnings during INSERT IGNORE (https://github.com/Shopify/lhm/pull/100)
data/lib/lhm/chunker.rb CHANGED
@@ -37,6 +37,8 @@ module Lhm
37
37
  end
38
38
 
39
39
  def execute
40
+ @start_time = Time.now
41
+
40
42
  return if @chunk_finder.table_empty?
41
43
  @next_to_insert = @start
42
44
  while @next_to_insert <= @limit || (@start == @limit)
@@ -47,6 +49,8 @@ module Lhm
47
49
  affected_rows = ChunkInsert.new(@migration, @connection, bottom, top, @options).insert_and_return_count_of_rows_created
48
50
  expected_rows = top - bottom + 1
49
51
 
52
+ rate_limited_log_progress
53
+
50
54
  if affected_rows < expected_rows
51
55
  raise_on_non_pk_duplicate_warning
52
56
  end
@@ -54,8 +58,10 @@ module Lhm
54
58
  if @throttler && affected_rows > 0
55
59
  @throttler.run
56
60
  end
57
- @printer.notify(bottom, @limit)
61
+
58
62
  @next_to_insert = top + 1
63
+ @printer.notify(bottom, @limit)
64
+
59
65
  break if @start == @limit
60
66
  end
61
67
  @printer.end
@@ -100,5 +106,14 @@ module Lhm
100
106
  return if @chunk_finder.table_empty?
101
107
  @chunk_finder.validate
102
108
  end
109
+
110
+ # Only log the chunker progress every 5 minutes instead of every iteration
111
+ def rate_limited_log_progress
112
+ current_time = Time.now
113
+ if current_time - @start_time > (5 * 60)
114
+ Lhm.logger.info("Inserted #{affected_rows} rows into the destination table from #{bottom} to #{top}")
115
+ @start_time = current_time
116
+ end
117
+ end
103
118
  end
104
119
  end
@@ -63,11 +63,12 @@ module Lhm
63
63
  retriable_connection.execute(ddl)
64
64
  end
65
65
  end
66
+ Lhm.logger.info("Dropped triggers on #{@lhm_triggers_for_origin.join(', ')}")
67
+ Lhm.logger.info("Dropped tables #{@lhm_triggers_for_origin.join(', ')}")
66
68
  end
67
69
 
68
70
  def report_ddls
69
- puts "The following DDLs would be executed:"
70
- ddls.each { |ddl| puts ddl }
71
+ Lhm.logger.info("The following DDLs would be executed: #{ddls}")
71
72
  end
72
73
  end
73
74
  end
data/lib/lhm/entangler.rb CHANGED
@@ -94,6 +94,7 @@ module Lhm
94
94
  retriable_connection.execute(stmt)
95
95
  end
96
96
  end
97
+ Lhm.logger.info("Created triggers on #{@origin.name}")
97
98
  end
98
99
 
99
100
  def after
@@ -102,6 +103,7 @@ module Lhm
102
103
  retriable_connection.execute(stmt)
103
104
  end
104
105
  end
106
+ Lhm.logger.info("Dropped triggers on #{@origin.name}")
105
107
  end
106
108
 
107
109
  def revert
data/lib/lhm/migrator.rb CHANGED
@@ -214,6 +214,8 @@ module Lhm
214
214
  replacement = %{CREATE TABLE `#{ @origin.destination_name }`}
215
215
  stmt = @origin.ddl.gsub(original, replacement)
216
216
  @connection.execute(tagged(stmt))
217
+
218
+ Lhm.logger.info("Created destination table #{@origin.destination_name}")
217
219
  end
218
220
 
219
221
  def destination_read
data/lib/lhm/printer.rb CHANGED
@@ -12,26 +12,30 @@ module Lhm
12
12
  end
13
13
  end
14
14
 
15
- class Percentage < Base
15
+ class Percentage
16
16
  def initialize
17
- super
18
17
  @max_length = 0
19
18
  end
20
19
 
21
20
  def notify(lowest, highest)
22
21
  return if !highest || highest == 0
22
+
23
+ # The argument lowest represents the next_to_insert row id, and highest represents the
24
+ # maximum id upto which chunker has to copy the data.
25
+ # If all the rows are inserted upto highest, then lowest passed here from chunker was
26
+ # highest + 1, which leads to the printer printing the progress > 100%.
27
+ return if lowest >= highest
28
+
23
29
  message = "%.2f%% (#{lowest}/#{highest}) complete" % (lowest.to_f / highest * 100.0)
24
30
  write(message)
25
31
  end
26
32
 
27
33
  def end
28
34
  write('100% complete')
29
- @output.write "\n"
30
35
  end
31
36
 
32
37
  def exception(e)
33
- write("failed: #{e}")
34
- @output.write "\n"
38
+ Lhm.logger.error("failed: #{e}")
35
39
  end
36
40
 
37
41
  private
@@ -42,7 +46,7 @@ module Lhm
42
46
  extra = 0
43
47
  end
44
48
 
45
- @output.write "\r#{message}" + (' ' * extra)
49
+ Lhm.logger.info(message)
46
50
  end
47
51
  end
48
52
 
data/lib/lhm/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Schmidt
3
3
 
4
4
  module Lhm
5
- VERSION = '3.4.0'
5
+ VERSION = '3.4.1'
6
6
  end
data/lib/lhm.rb CHANGED
@@ -114,17 +114,21 @@ module Lhm
114
114
  triggers.each do |trigger|
115
115
  connection.execute("drop trigger if exists #{trigger}")
116
116
  end
117
+ logger.info("Dropped triggers #{triggers.join(', ')}")
118
+
117
119
  tables.each do |table|
118
120
  connection.execute("drop table if exists #{table}")
119
121
  end
122
+ logger.info("Dropped tables #{tables.join(', ')}")
123
+
120
124
  true
121
125
  elsif tables.empty? && triggers.empty?
122
- puts 'Everything is clean. Nothing to do.'
126
+ logger.info('Everything is clean. Nothing to do.')
123
127
  true
124
128
  else
125
- puts "Would drop LHM backup tables: #{tables.join(', ')}."
126
- puts "Would drop LHM triggers: #{triggers.join(', ')}."
127
- puts 'Run with Lhm.cleanup(true) to drop all LHM triggers and tables, or Lhm.cleanup_current_run(true, table_name) to clean up a specific LHM.'
129
+ logger.info("Would drop LHM backup tables: #{tables.join(', ')}.")
130
+ logger.info("Would drop LHM triggers: #{triggers.join(', ')}.")
131
+ logger.info('Run with Lhm.cleanup(true) to drop all LHM triggers and tables, or Lhm.cleanup_current_run(true, table_name) to clean up a specific LHM.')
128
132
  false
129
133
  end
130
134
  end
@@ -58,7 +58,7 @@ describe Lhm::AtomicSwitcher do
58
58
  switcher.send :define_singleton_method, :atomic_switch do
59
59
  'SELECT * FROM nonexistent'
60
60
  end
61
- -> { switcher.run }.must_raise(ActiveRecord::StatementInvalid)
61
+ value(-> { switcher.run }).must_raise(ActiveRecord::StatementInvalid)
62
62
  end
63
63
 
64
64
  it "should raise when destination doesn't exist" do
@@ -75,8 +75,8 @@ describe Lhm::AtomicSwitcher do
75
75
  switcher.run
76
76
 
77
77
  slave do
78
- data_source_exists?(@origin).must_equal true
79
- table_read(@migration.archive_name).columns.keys.must_include 'origin'
78
+ value(data_source_exists?(@origin)).must_equal true
79
+ value(table_read(@migration.archive_name).columns.keys).must_include 'origin'
80
80
  end
81
81
  end
82
82
 
@@ -85,8 +85,8 @@ describe Lhm::AtomicSwitcher do
85
85
  switcher.run
86
86
 
87
87
  slave do
88
- data_source_exists?(@destination).must_equal false
89
- table_read(@origin.name).columns.keys.must_include 'destination'
88
+ value(data_source_exists?(@destination)).must_equal false
89
+ value(table_read(@origin.name).columns.keys).must_include 'destination'
90
90
  end
91
91
  end
92
92
  end
@@ -22,7 +22,7 @@ describe Lhm::ChunkInsert do
22
22
  @instance.insert_and_return_count_of_rows_created
23
23
 
24
24
  slave do
25
- count_all(@destination.name).must_equal(1)
25
+ value(count_all(@destination.name)).must_equal(1)
26
26
  end
27
27
  end
28
28
  end
@@ -29,7 +29,7 @@ describe Lhm::Chunker do
29
29
  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
30
30
 
31
31
  slave do
32
- count_all(@destination.name).must_equal(1)
32
+ value(count_all(@destination.name)).must_equal(1)
33
33
  end
34
34
 
35
35
  end
@@ -42,7 +42,7 @@ describe Lhm::Chunker do
42
42
  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
43
43
 
44
44
  slave do
45
- count_all(@destination.name).must_equal(2)
45
+ value(count_all(@destination.name)).must_equal(2)
46
46
  end
47
47
  end
48
48
 
@@ -58,7 +58,7 @@ describe Lhm::Chunker do
58
58
  Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
59
59
 
60
60
  slave do
61
- count_all(destination.name).must_equal(2)
61
+ value(count_all(destination.name)).must_equal(2)
62
62
  end
63
63
  end
64
64
 
@@ -128,7 +128,7 @@ describe Lhm::Chunker do
128
128
  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
129
129
 
130
130
  slave do
131
- count_all(@destination.name).must_equal(0)
131
+ value(count_all(@destination.name)).must_equal(0)
132
132
  end
133
133
 
134
134
  end
@@ -145,7 +145,7 @@ describe Lhm::Chunker do
145
145
  ).run
146
146
 
147
147
  slave do
148
- count_all(@destination.name).must_equal(23)
148
+ value(count_all(@destination.name)).must_equal(23)
149
149
  end
150
150
 
151
151
  printer.verify
@@ -161,7 +161,7 @@ describe Lhm::Chunker do
161
161
  ).run
162
162
 
163
163
  slave do
164
- count_all(@destination.name).must_equal(11)
164
+ value(count_all(@destination.name)).must_equal(11)
165
165
  end
166
166
 
167
167
  end
@@ -178,7 +178,7 @@ describe Lhm::Chunker do
178
178
  ).run
179
179
 
180
180
  slave do
181
- count_all(@destination.name).must_equal(23)
181
+ value(count_all(@destination.name)).must_equal(23)
182
182
  end
183
183
 
184
184
  printer.verify
@@ -203,7 +203,7 @@ describe Lhm::Chunker do
203
203
  assert_equal(Lhm::Throttler::SlaveLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
204
204
 
205
205
  slave do
206
- count_all(@destination.name).must_equal(15)
206
+ value(count_all(@destination.name)).must_equal(15)
207
207
  end
208
208
  end
209
209
 
@@ -238,7 +238,7 @@ describe Lhm::Chunker do
238
238
  assert_equal(0, throttler.send(:max_current_slave_lag))
239
239
 
240
240
  slave do
241
- count_all(@destination.name).must_equal(15)
241
+ value(count_all(@destination.name)).must_equal(15)
242
242
  end
243
243
 
244
244
  printer.verify
@@ -260,7 +260,7 @@ describe Lhm::Chunker do
260
260
  assert_match "Verification failed, aborting early", exception.message
261
261
 
262
262
  slave do
263
- count_all(@destination.name).must_equal(0)
263
+ value(count_all(@destination.name)).must_equal(0)
264
264
  end
265
265
  end
266
266
  end
@@ -31,12 +31,13 @@ describe Lhm, 'cleanup' do
31
31
 
32
32
  describe 'cleanup' do
33
33
  it 'should show temporary tables' do
34
- output = capture_stdout do
34
+ output = capture_stdout do |logger|
35
+ Lhm.logger = logger
35
36
  Lhm.cleanup
36
37
  end
37
- output.must_include('Would drop LHM backup tables')
38
- output.must_match(/lhma_[0-9_]*_users/)
39
- output.must_match(/lhma_[0-9_]*_permissions/)
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/)
40
41
  end
41
42
 
42
43
  it 'should show temporary tables within range' do
@@ -48,12 +49,13 @@ describe Lhm, 'cleanup' do
48
49
  table_name2 = Lhm::Migration.new(table2, nil, nil, {}, Time.now - 172800).archive_name
49
50
  table_rename(:permissions, table_name2)
50
51
 
51
- output = capture_stdout do
52
+ output = capture_stdout do |logger|
53
+ Lhm.logger = logger
52
54
  Lhm.cleanup false, { :until => Time.now - 86400 }
53
55
  end
54
- output.must_include('Would drop LHM backup tables')
55
- output.must_match(/lhma_[0-9_]*_users/)
56
- output.must_match(/lhma_[0-9_]*_permissions/)
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/)
57
59
  end
58
60
 
59
61
  it 'should exclude temporary tables outside range' do
@@ -65,30 +67,40 @@ describe Lhm, 'cleanup' do
65
67
  table_name2 = Lhm::Migration.new(table2, nil, nil, {}, Time.now).archive_name
66
68
  table_rename(:permissions, table_name2)
67
69
 
68
- output = capture_stdout do
70
+ output = capture_stdout do |logger|
71
+ Lhm.logger = logger
69
72
  Lhm.cleanup false, { :until => Time.now - 172800 }
70
73
  end
71
- output.must_include('Would drop LHM backup tables')
72
- output.wont_match(/lhma_[0-9_]*_users/)
73
- output.wont_match(/lhma_[0-9_]*_permissions/)
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/)
74
77
  end
75
78
 
76
79
  it 'should show temporary triggers' do
77
- output = capture_stdout do
80
+ output = capture_stdout do |logger|
81
+ Lhm.logger = logger
78
82
  Lhm.cleanup
79
83
  end
80
- output.must_include('Would drop LHM triggers')
81
- output.must_include('lhmt_ins_users')
82
- output.must_include('lhmt_del_users')
83
- output.must_include('lhmt_upd_users')
84
- output.must_include('lhmt_ins_permissions')
85
- output.must_include('lhmt_del_permissions')
86
- output.must_include('lhmt_upd_permissions')
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')
87
91
  end
88
92
 
89
93
  it 'should delete temporary tables' do
90
- Lhm.cleanup(true).must_equal(true)
91
- Lhm.cleanup.must_equal(true)
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')
92
104
  end
93
105
  end
94
106
 
@@ -96,27 +108,26 @@ describe Lhm, 'cleanup' do
96
108
  it 'should show lhmn table for the specified table only' do
97
109
  table_create(:permissions)
98
110
  table_rename(:permissions, 'lhmn_permissions')
99
- output = capture_stdout do
111
+ output = capture_stdout do |logger|
112
+ Lhm.logger = logger
100
113
  Lhm.cleanup_current_run(false, 'permissions')
101
- end.split("\n")
102
-
103
- assert_equal "The following DDLs would be executed:", output[0]
104
- assert_equal "drop trigger if exists lhmt_ins_permissions", output[1]
105
- assert_equal "drop trigger if exists lhmt_upd_permissions", output[2]
106
- assert_equal "drop trigger if exists lhmt_del_permissions", output[3]
107
- assert_match(/rename table lhmn_permissions to lhma_[0-9_]*_permissions_failed/, output[4])
108
- assert_equal 5, output.length
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")
109
120
  end
110
121
 
111
122
  it 'should show temporary triggers for the specified table only' do
112
- output = capture_stdout do
123
+ output = capture_stdout do |logger|
124
+ Lhm.logger = logger
113
125
  Lhm.cleanup_current_run(false, 'permissions')
114
- end.split("\n")
115
- assert_equal "The following DDLs would be executed:", output[0]
116
- assert_equal "drop trigger if exists lhmt_ins_permissions", output[1]
117
- assert_equal "drop trigger if exists lhmt_upd_permissions", output[2]
118
- assert_equal "drop trigger if exists lhmt_del_permissions", output[3]
119
- assert_equal 4, output.length
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")
120
131
  end
121
132
 
122
133
  it 'should delete temporary tables and triggers for the specified table only' do
@@ -26,7 +26,7 @@ describe Lhm::Entangler do
26
26
  end
27
27
 
28
28
  slave do
29
- count(:destination, 'common', 'inserted').must_equal(1)
29
+ value(count(:destination, 'common', 'inserted')).must_equal(1)
30
30
  end
31
31
  end
32
32
 
@@ -38,7 +38,7 @@ describe Lhm::Entangler do
38
38
  end
39
39
 
40
40
  slave do
41
- count(:destination, 'common', 'inserted').must_equal(0)
41
+ value(count(:destination, 'common', 'inserted')).must_equal(0)
42
42
  end
43
43
  end
44
44
 
@@ -49,7 +49,7 @@ describe Lhm::Entangler do
49
49
  end
50
50
 
51
51
  slave do
52
- count(:destination, 'common', 'updated').must_equal(1)
52
+ value(count(:destination, 'common', 'updated')).must_equal(1)
53
53
  end
54
54
  end
55
55
 
@@ -59,7 +59,7 @@ describe Lhm::Entangler do
59
59
  execute("insert into origin (common) values ('inserted')")
60
60
 
61
61
  slave do
62
- count(:destination, 'common', 'inserted').must_equal(0)
62
+ value(count(:destination, 'common', 'inserted')).must_equal(0)
63
63
  end
64
64
  end
65
65
  end
@@ -3,6 +3,7 @@
3
3
  require 'test_helper'
4
4
  require 'yaml'
5
5
  require 'active_support'
6
+ require 'logger'
6
7
 
7
8
  begin
8
9
  $db_config = YAML.load_file(File.expand_path(File.dirname(__FILE__)) + '/database.yml')
@@ -212,7 +213,8 @@ module IntegrationHelper
212
213
  def capture_stdout
213
214
  out = StringIO.new
214
215
  $stdout = out
215
- yield
216
+ logger = Logger.new($stdout)
217
+ yield logger
216
218
  return out.string
217
219
  ensure
218
220
  $stdout = ::STDOUT