lhm-shopify 3.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +34 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +183 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +216 -0
- data/Gemfile +5 -0
- data/LICENSE +27 -0
- data/README.md +284 -0
- data/Rakefile +22 -0
- data/bin/.gitkeep +0 -0
- data/dbdeployer/config.json +32 -0
- data/dbdeployer/install.sh +64 -0
- data/dev.yml +20 -0
- data/gemfiles/ar-2.3_mysql.gemfile +6 -0
- data/gemfiles/ar-3.2_mysql.gemfile +5 -0
- data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.0_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.1_mysql2.gemfile +5 -0
- data/gemfiles/ar-4.2_mysql2.gemfile +5 -0
- data/gemfiles/ar-5.0_mysql2.gemfile +5 -0
- data/lhm.gemspec +34 -0
- data/lib/lhm.rb +131 -0
- data/lib/lhm/atomic_switcher.rb +52 -0
- data/lib/lhm/chunk_finder.rb +32 -0
- data/lib/lhm/chunk_insert.rb +51 -0
- data/lib/lhm/chunker.rb +87 -0
- data/lib/lhm/cleanup/current.rb +74 -0
- data/lib/lhm/command.rb +48 -0
- data/lib/lhm/entangler.rb +117 -0
- data/lib/lhm/intersection.rb +51 -0
- data/lib/lhm/invoker.rb +98 -0
- data/lib/lhm/locked_switcher.rb +74 -0
- data/lib/lhm/migration.rb +43 -0
- data/lib/lhm/migrator.rb +237 -0
- data/lib/lhm/printer.rb +59 -0
- data/lib/lhm/railtie.rb +9 -0
- data/lib/lhm/sql_helper.rb +77 -0
- data/lib/lhm/sql_retry.rb +61 -0
- data/lib/lhm/table.rb +121 -0
- data/lib/lhm/table_name.rb +23 -0
- data/lib/lhm/test_support.rb +35 -0
- data/lib/lhm/throttler.rb +36 -0
- data/lib/lhm/throttler/slave_lag.rb +145 -0
- data/lib/lhm/throttler/threads_running.rb +53 -0
- data/lib/lhm/throttler/time.rb +29 -0
- data/lib/lhm/timestamp.rb +11 -0
- data/lib/lhm/version.rb +6 -0
- data/shipit.rubygems.yml +0 -0
- data/spec/.lhm.example +4 -0
- data/spec/README.md +58 -0
- data/spec/fixtures/bigint_table.ddl +4 -0
- data/spec/fixtures/composite_primary_key.ddl +7 -0
- data/spec/fixtures/custom_primary_key.ddl +6 -0
- data/spec/fixtures/destination.ddl +6 -0
- data/spec/fixtures/lines.ddl +7 -0
- data/spec/fixtures/origin.ddl +6 -0
- data/spec/fixtures/permissions.ddl +5 -0
- data/spec/fixtures/small_table.ddl +4 -0
- data/spec/fixtures/tracks.ddl +5 -0
- data/spec/fixtures/users.ddl +14 -0
- data/spec/fixtures/wo_id_int_column.ddl +6 -0
- data/spec/integration/atomic_switcher_spec.rb +93 -0
- data/spec/integration/chunk_insert_spec.rb +29 -0
- data/spec/integration/chunker_spec.rb +185 -0
- data/spec/integration/cleanup_spec.rb +136 -0
- data/spec/integration/entangler_spec.rb +66 -0
- data/spec/integration/integration_helper.rb +237 -0
- data/spec/integration/invoker_spec.rb +33 -0
- data/spec/integration/lhm_spec.rb +585 -0
- data/spec/integration/lock_wait_timeout_spec.rb +30 -0
- data/spec/integration/locked_switcher_spec.rb +50 -0
- data/spec/integration/sql_retry/lock_wait_spec.rb +125 -0
- data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +101 -0
- data/spec/integration/table_spec.rb +91 -0
- data/spec/test_helper.rb +32 -0
- data/spec/unit/atomic_switcher_spec.rb +31 -0
- data/spec/unit/chunk_finder_spec.rb +73 -0
- data/spec/unit/chunk_insert_spec.rb +44 -0
- data/spec/unit/chunker_spec.rb +166 -0
- data/spec/unit/entangler_spec.rb +124 -0
- data/spec/unit/intersection_spec.rb +51 -0
- data/spec/unit/lhm_spec.rb +29 -0
- data/spec/unit/locked_switcher_spec.rb +51 -0
- data/spec/unit/migrator_spec.rb +146 -0
- data/spec/unit/printer_spec.rb +97 -0
- data/spec/unit/sql_helper_spec.rb +32 -0
- data/spec/unit/table_name_spec.rb +39 -0
- data/spec/unit/table_spec.rb +47 -0
- data/spec/unit/throttler/slave_lag_spec.rb +317 -0
- data/spec/unit/throttler/threads_running_spec.rb +64 -0
- data/spec/unit/throttler_spec.rb +124 -0
- data/spec/unit/unit_helper.rb +13 -0
- metadata +239 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require 'lhm/intersection'
|
5
|
+
require 'lhm/timestamp'
|
6
|
+
|
7
|
+
module Lhm
|
8
|
+
class Migration
|
9
|
+
attr_reader :origin, :destination, :conditions, :renames
|
10
|
+
|
11
|
+
def initialize(origin, destination, conditions = nil, renames = {}, time = Time.now)
|
12
|
+
@origin = origin
|
13
|
+
@destination = destination
|
14
|
+
@conditions = conditions
|
15
|
+
@renames = renames
|
16
|
+
@table_name = TableName.new(@origin.name, time)
|
17
|
+
end
|
18
|
+
|
19
|
+
def archive_name
|
20
|
+
@archive_name ||= @table_name.archived
|
21
|
+
end
|
22
|
+
|
23
|
+
def intersection
|
24
|
+
Intersection.new(@origin, @destination, @renames)
|
25
|
+
end
|
26
|
+
|
27
|
+
def origin_name
|
28
|
+
@table_name.original
|
29
|
+
end
|
30
|
+
|
31
|
+
def origin_columns
|
32
|
+
@origin_columns ||= intersection.origin.typed(origin_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def destination_name
|
36
|
+
@destination_name ||= destination.name
|
37
|
+
end
|
38
|
+
|
39
|
+
def destination_columns
|
40
|
+
@destination_columns ||= intersection.destination.joined
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/lhm/migrator.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require 'lhm/command'
|
5
|
+
require 'lhm/migration'
|
6
|
+
require 'lhm/sql_helper'
|
7
|
+
require 'lhm/table'
|
8
|
+
|
9
|
+
module Lhm
|
10
|
+
# Copies existing schema and applies changes using alter on the empty table.
|
11
|
+
# `run` returns a Migration which can be used for the remaining process.
|
12
|
+
class Migrator
|
13
|
+
include Command
|
14
|
+
include SqlHelper
|
15
|
+
|
16
|
+
attr_reader :name, :statements, :connection, :conditions, :renames, :origin
|
17
|
+
|
18
|
+
def initialize(table, connection = nil)
|
19
|
+
@connection = connection
|
20
|
+
@origin = table
|
21
|
+
@name = table.destination_name
|
22
|
+
@statements = []
|
23
|
+
@renames = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Alter a table with a custom statement
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
#
|
30
|
+
# Lhm.change_table(:users) do |m|
|
31
|
+
# m.ddl("ALTER TABLE #{m.name} ADD COLUMN age INT(11)")
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @param [String] statement SQL alter statement
|
35
|
+
# @note
|
36
|
+
#
|
37
|
+
# Don't write the table name directly into the statement. Use the #name
|
38
|
+
# getter instead, because the alter statement will be executed against a
|
39
|
+
# temporary table.
|
40
|
+
#
|
41
|
+
def ddl(statement)
|
42
|
+
statements << statement
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add a column to a table
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
#
|
49
|
+
# Lhm.change_table(:users) do |m|
|
50
|
+
# m.add_column(:comment, "VARCHAR(12) DEFAULT '0'")
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @param [String] name Name of the column to add
|
54
|
+
# @param [String] definition Valid SQL column definition
|
55
|
+
def add_column(name, definition)
|
56
|
+
ddl('alter table `%s` add column `%s` %s' % [@name, name, definition])
|
57
|
+
end
|
58
|
+
|
59
|
+
# Change an existing column to a new definition
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
#
|
63
|
+
# Lhm.change_table(:users) do |m|
|
64
|
+
# m.change_column(:comment, "VARCHAR(12) DEFAULT '0' NOT NULL")
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# @param [String] name Name of the column to change
|
68
|
+
# @param [String] definition Valid SQL column definition
|
69
|
+
def change_column(name, definition)
|
70
|
+
ddl('alter table `%s` modify column `%s` %s' % [@name, name, definition])
|
71
|
+
end
|
72
|
+
|
73
|
+
# Rename an existing column.
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
#
|
77
|
+
# Lhm.change_table(:users) do |m|
|
78
|
+
# m.rename_column(:login, :username)
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @param [String] old Name of the column to change
|
82
|
+
# @param [String] nu New name to use for the column
|
83
|
+
def rename_column(old, nu)
|
84
|
+
col = @origin.columns[old.to_s]
|
85
|
+
|
86
|
+
definition = col[:type]
|
87
|
+
|
88
|
+
definition += ' NOT NULL' unless col[:is_nullable] == "YES"
|
89
|
+
definition += " DEFAULT #{@connection.quote(col[:column_default])}" if col[:column_default]
|
90
|
+
definition += " COMMENT #{@connection.quote(col[:comment])}" if col[:comment]
|
91
|
+
definition += " COLLATE #{@connection.quote(col[:collate])}" if col[:collate]
|
92
|
+
|
93
|
+
ddl('alter table `%s` change column `%s` `%s` %s' % [@name, old, nu, definition])
|
94
|
+
@renames[old.to_s] = nu.to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
# Remove a column from a table
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
#
|
101
|
+
# Lhm.change_table(:users) do |m|
|
102
|
+
# m.remove_column(:comment)
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# @param [String] name Name of the column to delete
|
106
|
+
def remove_column(name)
|
107
|
+
ddl('alter table `%s` drop `%s`' % [@name, name])
|
108
|
+
end
|
109
|
+
|
110
|
+
# Add an index to a table
|
111
|
+
#
|
112
|
+
# @example
|
113
|
+
#
|
114
|
+
# Lhm.change_table(:users) do |m|
|
115
|
+
# m.add_index(:comment)
|
116
|
+
# m.add_index([:username, :created_at])
|
117
|
+
# m.add_index("comment(10)")
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# @param [String, Symbol, Array<String, Symbol>] columns
|
121
|
+
# A column name given as String or Symbol. An Array of Strings or Symbols
|
122
|
+
# for compound indexes. It's possible to pass a length limit.
|
123
|
+
# @param [String, Symbol] index_name
|
124
|
+
# Optional name of the index to be created
|
125
|
+
def add_index(columns, index_name = nil)
|
126
|
+
ddl(index_ddl(columns, false, index_name))
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add a unique index to a table
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
#
|
133
|
+
# Lhm.change_table(:users) do |m|
|
134
|
+
# m.add_unique_index(:comment)
|
135
|
+
# m.add_unique_index([:username, :created_at])
|
136
|
+
# m.add_unique_index("comment(10)")
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# @param [String, Symbol, Array<String, Symbol>] columns
|
140
|
+
# A column name given as String or Symbol. An Array of Strings or Symbols
|
141
|
+
# for compound indexes. It's possible to pass a length limit.
|
142
|
+
# @param [String, Symbol] index_name
|
143
|
+
# Optional name of the index to be created
|
144
|
+
def add_unique_index(columns, index_name = nil)
|
145
|
+
ddl(index_ddl(columns, true, index_name))
|
146
|
+
end
|
147
|
+
|
148
|
+
# Remove an index from a table
|
149
|
+
#
|
150
|
+
# @example
|
151
|
+
#
|
152
|
+
# Lhm.change_table(:users) do |m|
|
153
|
+
# m.remove_index(:comment)
|
154
|
+
# m.remove_index([:username, :created_at])
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# @param [String, Symbol, Array<String, Symbol>] columns
|
158
|
+
# A column name given as String or Symbol. An Array of Strings or Symbols
|
159
|
+
# for compound indexes.
|
160
|
+
# @param [String, Symbol] index_name
|
161
|
+
# Optional name of the index to be removed
|
162
|
+
def remove_index(columns, index_name = nil)
|
163
|
+
columns = [columns].flatten.map(&:to_sym)
|
164
|
+
from_origin = @origin.indices.find { |_, cols| cols.map(&:to_sym) == columns }
|
165
|
+
index_name ||= from_origin[0] unless from_origin.nil?
|
166
|
+
index_name ||= idx_name(@origin.name, columns)
|
167
|
+
ddl('drop index `%s` on `%s`' % [index_name, @name])
|
168
|
+
end
|
169
|
+
|
170
|
+
# Filter the data that is copied into the new table by the provided SQL.
|
171
|
+
# This SQL will be inserted into the copy directly after the "from"
|
172
|
+
# statement - so be sure to use inner/outer join syntax and not cross joins.
|
173
|
+
#
|
174
|
+
# @example Add a conditions filter to the migration.
|
175
|
+
# Lhm.change_table(:sounds) do |m|
|
176
|
+
# m.filter("inner join users on users.`id` = sounds.`user_id` and sounds.`public` = 1")
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# @param [ String ] sql The sql filter.
|
180
|
+
#
|
181
|
+
# @return [ String ] The sql filter.
|
182
|
+
def filter(sql)
|
183
|
+
@conditions = sql
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def validate
|
189
|
+
unless @connection.data_source_exists?(@origin.name)
|
190
|
+
error("could not find origin table #{ @origin.name }")
|
191
|
+
end
|
192
|
+
|
193
|
+
unless @origin.satisfies_id_column_requirement?
|
194
|
+
error('origin does not satisfy `id` key requirements')
|
195
|
+
end
|
196
|
+
|
197
|
+
dest = @origin.destination_name
|
198
|
+
|
199
|
+
if @connection.data_source_exists?(dest)
|
200
|
+
error("#{ dest } should not exist; not cleaned up from previous run?")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def execute
|
205
|
+
destination_create
|
206
|
+
@statements.each do |stmt|
|
207
|
+
@connection.execute(tagged(stmt))
|
208
|
+
end
|
209
|
+
Migration.new(@origin, destination_read, conditions, renames)
|
210
|
+
end
|
211
|
+
|
212
|
+
def destination_create
|
213
|
+
original = %{CREATE TABLE `#{ @origin.name }`}
|
214
|
+
replacement = %{CREATE TABLE `#{ @origin.destination_name }`}
|
215
|
+
stmt = @origin.ddl.gsub(original, replacement)
|
216
|
+
@connection.execute(tagged(stmt))
|
217
|
+
end
|
218
|
+
|
219
|
+
def destination_read
|
220
|
+
Table.parse(@origin.destination_name, connection)
|
221
|
+
end
|
222
|
+
|
223
|
+
def index_ddl(cols, unique = nil, index_name = nil)
|
224
|
+
assert_valid_idx_name(index_name)
|
225
|
+
type = unique ? 'unique index' : 'index'
|
226
|
+
index_name ||= idx_name(@origin.name, cols)
|
227
|
+
parts = [type, index_name, @name, idx_spec(cols)]
|
228
|
+
'create %s `%s` on `%s` (%s)' % parts
|
229
|
+
end
|
230
|
+
|
231
|
+
def assert_valid_idx_name(index_name)
|
232
|
+
if index_name && !(index_name.is_a?(String) || index_name.is_a?(Symbol))
|
233
|
+
raise ArgumentError, 'index_name must be a string or symbol'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
data/lib/lhm/printer.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Lhm
|
2
|
+
module Printer
|
3
|
+
class Output
|
4
|
+
def write(message)
|
5
|
+
print message
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Base
|
10
|
+
def initialize
|
11
|
+
@output = Output.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Percentage < Base
|
16
|
+
def initialize
|
17
|
+
super
|
18
|
+
@max_length = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify(lowest, highest)
|
22
|
+
return if !highest || highest == 0
|
23
|
+
message = "%.2f%% (#{lowest}/#{highest}) complete" % (lowest.to_f / highest * 100.0)
|
24
|
+
write(message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def end
|
28
|
+
write('100% complete')
|
29
|
+
@output.write "\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
def exception(e)
|
33
|
+
write("failed: #{e}")
|
34
|
+
@output.write "\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def write(message)
|
40
|
+
if (extra = @max_length - message.length) < 0
|
41
|
+
@max_length = message.length
|
42
|
+
extra = 0
|
43
|
+
end
|
44
|
+
|
45
|
+
@output.write "\r#{message}" + (' ' * extra)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Dot < Base
|
50
|
+
def notify(*)
|
51
|
+
@output.write '.'
|
52
|
+
end
|
53
|
+
|
54
|
+
def end
|
55
|
+
@output.write "\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/lhm/railtie.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
module Lhm
|
5
|
+
module SqlHelper
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def annotation
|
9
|
+
'/* large hadron migration */'
|
10
|
+
end
|
11
|
+
|
12
|
+
def idx_name(table_name, cols)
|
13
|
+
column_names = column_definition(cols).map(&:first)
|
14
|
+
"index_#{ table_name }_on_#{ column_names.join('_and_') }"
|
15
|
+
end
|
16
|
+
|
17
|
+
def idx_spec(cols)
|
18
|
+
column_definition(cols).map do |name, length|
|
19
|
+
"`#{ name }`#{ length }"
|
20
|
+
end.join(', ')
|
21
|
+
end
|
22
|
+
|
23
|
+
def version_string
|
24
|
+
row = connection.select_one("show variables like 'version'")
|
25
|
+
value = struct_key(row, 'Value')
|
26
|
+
row[value]
|
27
|
+
end
|
28
|
+
|
29
|
+
def tagged(statement)
|
30
|
+
"#{ statement } #{ SqlHelper.annotation }"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def column_definition(cols)
|
36
|
+
Array(cols).map do |column|
|
37
|
+
column.to_s.match(/`?([^\(]+)`?(\([^\)]+\))?/).captures
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Older versions of MySQL contain an atomic rename bug affecting bin
|
42
|
+
# log order. Affected versions extracted from bug report:
|
43
|
+
#
|
44
|
+
# http://bugs.mysql.com/bug.php?id=39675
|
45
|
+
#
|
46
|
+
# More Info: http://dev.mysql.com/doc/refman/5.5/en/metadata-locking.html
|
47
|
+
def supports_atomic_switch?
|
48
|
+
major, minor, tiny = version_string.split('.').map(&:to_i)
|
49
|
+
|
50
|
+
case major
|
51
|
+
when 4 then return false if minor and minor < 2
|
52
|
+
when 5
|
53
|
+
case minor
|
54
|
+
when 0 then return false if tiny and tiny < 52
|
55
|
+
when 1 then return false
|
56
|
+
when 4 then return false if tiny and tiny < 4
|
57
|
+
when 5 then return false if tiny and tiny < 3
|
58
|
+
end
|
59
|
+
when 6
|
60
|
+
case minor
|
61
|
+
when 0 then return false if tiny and tiny < 11
|
62
|
+
end
|
63
|
+
end
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def struct_key(struct, key)
|
68
|
+
keys = if struct.is_a? Hash
|
69
|
+
struct.keys
|
70
|
+
else
|
71
|
+
struct.members
|
72
|
+
end
|
73
|
+
|
74
|
+
keys.find { |k| k.to_s.downcase == key.to_s.downcase }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'retriable'
|
2
|
+
require 'lhm/sql_helper'
|
3
|
+
|
4
|
+
module Lhm
|
5
|
+
# SqlRetry standardizes the interface for retry behavior in components like
|
6
|
+
# Entangler, AtomicSwitcher, ChunkerInsert.
|
7
|
+
#
|
8
|
+
# By default if an error includes the message "Lock wait timeout exceeded", or
|
9
|
+
# "Deadlock found when trying to get lock", SqlRetry will retry again
|
10
|
+
# once the MySQL client returns control to the caller, plus one second.
|
11
|
+
# It will retry a total of 10 times and output to the logger a description
|
12
|
+
# of the retry with error information, retry count, and elapsed time.
|
13
|
+
#
|
14
|
+
# This behavior can be modified by passing `options` that are documented in
|
15
|
+
# https://github.com/kamui/retriable. Additionally, a "log_prefix" option,
|
16
|
+
# which is unique to SqlRetry can be used to prefix log output.
|
17
|
+
class SqlRetry
|
18
|
+
def initialize(connection, options = {})
|
19
|
+
@connection = connection
|
20
|
+
@log_prefix = options.delete(:log_prefix)
|
21
|
+
@retry_config = default_retry_config.dup.merge!(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_retries
|
25
|
+
Retriable.retriable(retry_config) do
|
26
|
+
yield(@connection)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :retry_config
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# For a full list of configuration options see https://github.com/kamui/retriable
|
35
|
+
def default_retry_config
|
36
|
+
{
|
37
|
+
on: {
|
38
|
+
StandardError => [
|
39
|
+
/Lock wait timeout exceeded/,
|
40
|
+
/Timeout waiting for a response from the last query/,
|
41
|
+
/Deadlock found when trying to get lock/,
|
42
|
+
/Query execution was interrupted/,
|
43
|
+
/Lost connection to MySQL server during query/,
|
44
|
+
/Max connect timeout reached/,
|
45
|
+
/Unknown MySQL server host/,
|
46
|
+
]
|
47
|
+
},
|
48
|
+
multiplier: 1, # each successive interval grows by this factor
|
49
|
+
base_interval: 1, # the initial interval in seconds between tries.
|
50
|
+
tries: 20, # Number of attempts to make at running your code block (includes initial attempt).
|
51
|
+
rand_factor: 0, # percentage to randomize the next retry interval time
|
52
|
+
max_elapsed_time: Float::INFINITY, # max total time in seconds that code is allowed to keep being retried
|
53
|
+
on_retry: Proc.new do |exception, try_number, total_elapsed_time, next_interval|
|
54
|
+
log = "#{exception.class}: '#{exception.message}' - #{try_number} tries in #{total_elapsed_time} seconds and #{next_interval} seconds until the next try."
|
55
|
+
log.prepend("[#{@log_prefix}] ") if @log_prefix
|
56
|
+
Lhm.logger.info(log)
|
57
|
+
end
|
58
|
+
}.freeze
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|