diff --git a/CHANGELOG.md b/CHANGELOG.md index 663a870c..cc6b10d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 3.5.4 (Dec, 2021) +* Refactored the way options are handled internally. Code is now much clearer to understand +* Removed optional connection_options from `Lhm.setup` and `Lhm.connection` +* Option `reconnect_with_consistent_host` will now be provided with `options` for `Lhm.change_table` +* Option `disable_proxysql_tags` introduced as part of `options` for `Lhm.change_table`. This will deactivate + the internal proxysql tagging of queries and will use the ones provided by ActiveRecord. + +# 3.5.3 (Dec, 2021) +* Adds ProxySQL comments at the end of query to accommodate for internal tool's requirements + # 3.5.2 (Dec, 2021) * Fixed error on undefined connection, when calling `Lhm.connection` without calling `Lhm.setup` first * Changed `Lhm.connection.connection` to `lhm.connection.ar_connection` for increased clarity and readability diff --git a/Gemfile.lock b/Gemfile.lock index 6ea71115..647caca7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - lhm-shopify (3.5.3) + lhm-shopify (3.5.4) retriable (>= 3.0.0) GEM diff --git a/README.md b/README.md index d4f87cfc..f8677066 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,8 @@ tables must be cleaned up. LHM can recover from connection loss. However, when used in conjunction with ProxySQL, there are multiple ways that connection loss could induce data loss (if triggered by a failover). Therefore it will perform additional checks to ensure that the MySQL host stays consistent across the schema migrations if the feature is enabled. -This is done by tagging every query with `/*maintenance:lhm*/`, which will be recognized by ProxySQL. -However, to get this feature working, a new ProxySQL query rule must be added. +This is done by tagging every query with `/*maintenance:lhm*/`, which will be recognized by ProxySQL (these tags +can be disabled). However, to get this feature working, a new ProxySQL query rule must be added. ```cnf { rule_id = @@ -145,12 +145,21 @@ forwarded to the right target. ``` Once these changes are added to the ProxySQL configuration (either through `.cnf` or dynamically through the admin interface), -the feature can be enabled. This is done by adding this flag when doing the initial setup: +the feature can be enabled. This is done by adding this flag when providing options to the migration: ```ruby - Lhm.setup(connection, options: {reconnect_with_consistent_host: true}) + Lhm.change_table(..., options: {reconnect_with_consistent_host: true}) do |t| + ... +end ``` **Note**: This feature is disabled by default +The annotations mentioned above can also be disabled if ActiveRecord's QueryLogs or Marginalia are enabled . This is done by providing +the following option to `Lhm.change_table`. +```ruby + Lhm.change_table(..., options: {disable_proxysql_tags: true}) do |t| + ... +end +``` ## Throttler LHM uses a throttling mechanism to read data in your original table. By default, 2,000 rows are read each 0.1 second. If you want to change that behaviour, you can pass an instance of a throttler with the `throttler` option. In this example, 1,000 rows will be read with a 10 second delay between each processing: diff --git a/Rakefile b/Rakefile index f07e79bd..2b3f063b 100644 --- a/Rakefile +++ b/Rakefile @@ -16,15 +16,16 @@ Rake::TestTask.new('integration') do |t| t.libs << 'spec' t.test_files = FileList['spec/integration/**/*_spec.rb'] t.verbose = true - end +end Rake::TestTask.new('dev') do |t| t.libs << 'lib' t.libs << 'spec' - t.test_files = FileList[ - 'spec/test_helper.rb', - # Add file to test individually - ] + + files = FileList.new('spec/test_helper.rb') + files.add(ENV["SINGLE_TEST"]) if ENV["SINGLE_TEST"] + t.test_files = files + t.verbose = true end diff --git a/dev.yml b/dev.yml index 16f45cfe..9a54f2ab 100644 --- a/dev.yml +++ b/dev.yml @@ -5,7 +5,7 @@ up: or: [mysql@5.7] conflicts: [shopify/shopify/mysql-client, mysql-connector-c, mysql, mysql-client] - wget - - ruby: 2.7.0 + - ruby: 2.7.5 - bundler - custom: name: Get Appraisal gems @@ -31,7 +31,7 @@ commands: if [[ $# -eq 0 ]]; then bundle exec rake unit && bundle exec rake integration else - bundle exec rake dev TEST="$@" + SINGLE_TEST="$@" bundle exec rake dev fi appraisals: bundle exec appraisal rake specs cov: rm -rf coverage; COV=1 bundle exec rake unit && bundle exec rake integration; open coverage/index.html diff --git a/gemfiles/activerecord_5.2.gemfile.lock b/gemfiles/activerecord_5.2.gemfile.lock index 55221dea..9f96c39c 100644 --- a/gemfiles/activerecord_5.2.gemfile.lock +++ b/gemfiles/activerecord_5.2.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - lhm-shopify (3.5.3) + lhm-shopify (3.5.4) retriable (>= 3.0.0) GEM diff --git a/gemfiles/activerecord_6.0.gemfile.lock b/gemfiles/activerecord_6.0.gemfile.lock index e0e59e49..9e752d0b 100644 --- a/gemfiles/activerecord_6.0.gemfile.lock +++ b/gemfiles/activerecord_6.0.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - lhm-shopify (3.5.3) + lhm-shopify (3.5.4) retriable (>= 3.0.0) GEM diff --git a/gemfiles/activerecord_6.1.gemfile.lock b/gemfiles/activerecord_6.1.gemfile.lock index 79b8df22..3a68d629 100644 --- a/gemfiles/activerecord_6.1.gemfile.lock +++ b/gemfiles/activerecord_6.1.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - lhm-shopify (3.5.3) + lhm-shopify (3.5.4) retriable (>= 3.0.0) GEM diff --git a/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock b/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock index eced8c5f..5b80eaca 100644 --- a/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +++ b/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - lhm-shopify (3.5.3) + lhm-shopify (3.5.4) retriable (>= 3.0.0) GEM diff --git a/lib/lhm.rb b/lib/lhm.rb index 7a995b5b..3c2b0607 100644 --- a/lib/lhm.rb +++ b/lib/lhm.rb @@ -46,15 +46,21 @@ module Lhm # Use atomic switch to rename tables (defaults to: true) # If using a version of mysql affected by atomic switch bug, LHM forces user # to set this option (see SqlHelper#supports_atomic_switch?) + # @option options [Boolean] :reconnect_with_consistent_host + # Active / Deactivate ProxySQL-aware reconnection procedure (default to: false) + # @option options [Boolean] :disable_proxysql_tags + # Active / Deactivate ProxySQL tags to be added to queries (if used with Marginalia or ActiveRecord QueryLogs) # @yield [Migrator] Yielded Migrator object records the changes # @return [Boolean] Returns true if the migration finishes # @raise [Error] Raises Lhm::Error in case of a error and aborts the migration def change_table(table_name, options = {}, &block) - origin = Table.parse(table_name, connection) - invoker = Invoker.new(origin, connection) - block.call(invoker.migrator) - invoker.run(options) - true + with_flags(options) do + origin = Table.parse(table_name, connection) + invoker = Invoker.new(origin, connection) + block.call(invoker.migrator) + invoker.run(options) + true + end end # Cleanup tables and triggers @@ -86,27 +92,16 @@ def cleanup_current_run(run, table_name, options = {}) # Setups DB connection # # @param [ActiveRecord::Base] connection ActiveRecord Connection - # @param [Hash] connection_options Optional options (defaults to: empty hash) - # @option connection_options [Boolean] :reconnect_with_consistent_host - # Active / Deactivate ProxySQL-aware reconnection procedure (default to: false) - def setup(connection, connection_options = {}) - @@connection = Connection.new(connection: connection, options: connection_options) + def setup(connection) + @@connection = Connection.new(connection: connection) end - # Setups DB connection - # - # @param [Hash] connection_options Optional options (defaults to: empty hash) - # @option connection_options [Boolean] :reconnect_with_consistent_host - # Active / Deactivate ProxySQL-aware reconnection procedure (default to: false) - def connection(connection_options = nil) + # Returns DB connection (or initializes it if not created yet) + def connection @@connection ||= begin - raise 'Please call Lhm.setup' unless defined?(ActiveRecord) - @@connection = Connection.new(connection: ActiveRecord::Base.connection, options: connection_options || {}) - end - - @@connection.process_connection_options(connection_options) unless connection_options.nil? - - @@connection + raise 'Please call Lhm.setup' unless defined?(ActiveRecord) + @@connection = Connection.new(connection: ActiveRecord::Base.connection) + end end def self.logger=(new_logger) @@ -148,4 +143,19 @@ def drop_tables_and_triggers(run = false, triggers, tables) false end end + + def with_flags(options) + old_flags = { + reconnect_with_consistent_host: Lhm.connection.reconnect_with_consistent_host, + disable_proxysql_tags: ProxySQLHelper.disable_tags + } + + Lhm.connection.reconnect_with_consistent_host = options[:reconnect_with_consistent_host] || false + ProxySQLHelper.disable_tags = options[:disable_proxysql_tags] || false + + yield + ensure + Lhm.connection.reconnect_with_consistent_host = old_flags[:reconnect_with_consistent_host] + ProxySQLHelper.disable_tags = old_flags[:disable_proxysql_tags] + end end diff --git a/lib/lhm/atomic_switcher.rb b/lib/lhm/atomic_switcher.rb index 5ea4e1ac..f4346dd0 100644 --- a/lib/lhm/atomic_switcher.rb +++ b/lib/lhm/atomic_switcher.rb @@ -16,12 +16,13 @@ class AtomicSwitcher attr_reader :connection - def initialize(migration, connection = nil, options={}) + LOG_PREFIX = "AtomicSwitcher" + + def initialize(migration, connection = nil) @migration = migration @connection = connection @origin = migration.origin @destination = migration.destination - @retry_options = options[:retriable] || {} end def atomic_switch @@ -39,7 +40,7 @@ def validate private def execute - @connection.execute(atomic_switch, should_retry: true, retry_options: @retry_options) + @connection.execute(atomic_switch, should_retry: true, log_prefix: LOG_PREFIX) end end end diff --git a/lib/lhm/chunk_insert.rb b/lib/lhm/chunk_insert.rb index 0d3b42be..2a8959d5 100644 --- a/lib/lhm/chunk_insert.rb +++ b/lib/lhm/chunk_insert.rb @@ -3,6 +3,9 @@ module Lhm class ChunkInsert + + LOG_PREFIX = "ChunkInsert" + def initialize(migration, connection, lowest, highest, retry_options = {}) @migration = migration @connection = connection @@ -12,7 +15,7 @@ def initialize(migration, connection, lowest, highest, retry_options = {}) end def insert_and_return_count_of_rows_created - @connection.update(sql, should_retry: true, retry_options: @retry_options) + @connection.update(sql, should_retry: true, log_prefix: LOG_PREFIX) end def sql diff --git a/lib/lhm/chunker.rb b/lib/lhm/chunker.rb index 2e46a4ca..764a4e09 100644 --- a/lib/lhm/chunker.rb +++ b/lib/lhm/chunker.rb @@ -13,6 +13,8 @@ class Chunker attr_reader :connection + LOG_PREFIX = "Chunker" + # Copy from origin to destination in chunks of size `stride`. # Use the `throttler` class to sleep between each stride. def initialize(migration, connection = nil, options = {}) @@ -31,9 +33,7 @@ def initialize(migration, connection = nil, options = {}) @retry_options = options[:retriable] || {} @retry_helper = SqlRetry.new( @connection, - retry_options: { - log_prefix: "Chunker" - }.merge!(@retry_options) + retry_options: @retry_options ) end @@ -79,7 +79,7 @@ def execute private def raise_on_non_pk_duplicate_warning - @connection.execute("show warnings", should_retry: true, retry_options: @retry_options).each do |level, code, message| + @connection.execute("show warnings", should_retry: true, log_prefix: LOG_PREFIX).each do |level, code, message| unless message.match?(/Duplicate entry .+ for key 'PRIMARY'/) m = "Unexpected warning found for inserted row: #{message}" Lhm.logger.warn(m) @@ -94,14 +94,14 @@ def bottom def verify_can_run return unless @verifier - @retry_helper.with_retries(@retry_options) do |retriable_connection| + @retry_helper.with_retries(log_prefix: LOG_PREFIX) do |retriable_connection| raise "Verification failed, aborting early" if !@verifier.call(retriable_connection) end end def upper_id(next_id, stride) sql = "select id from `#{ @migration.origin_name }` where id >= #{ next_id } order by id limit 1 offset #{ stride - 1}" - top = @connection.select_value(sql, should_retry: true, retry_options: @retry_options) + top = @connection.select_value(sql, should_retry: true, log_prefix: LOG_PREFIX) [top ? top.to_i : @limit, @limit].min end diff --git a/lib/lhm/cleanup/current.rb b/lib/lhm/cleanup/current.rb index 39c3840e..a0caa8a9 100644 --- a/lib/lhm/cleanup/current.rb +++ b/lib/lhm/cleanup/current.rb @@ -4,6 +4,9 @@ module Lhm module Cleanup class Current + + LOG_PREFIX = "Current" + def initialize(run, origin_table_name, connection, options={}) @run = run @table_name = TableName.new(origin_table_name) @@ -54,7 +57,7 @@ def lhmn_tables_for_origin def execute_ddls ddls.each do |ddl| - @connection.execute(ddl, should_retry: true, retry_options: @retry_config) + @connection.execute(ddl, should_retry: true, log_prefix: LOG_PREFIX) end Lhm.logger.info("Dropped triggers on #{@lhm_triggers_for_origin.join(', ')}") Lhm.logger.info("Dropped tables #{@lhm_triggers_for_origin.join(', ')}") diff --git a/lib/lhm/connection.rb b/lib/lhm/connection.rb index 1139f904..452fb480 100644 --- a/lib/lhm/connection.rb +++ b/lib/lhm/connection.rb @@ -6,8 +6,11 @@ module Lhm # connection. class Connection < SimpleDelegator - def initialize(connection:, default_log_prefix: nil, options: {}) - @default_log_prefix = default_log_prefix + # These attributes have writers defined in this file + attr_reader :retry_config + alias ar_connection __getobj__ + + def initialize(connection:, options: {}) @sql_retry = Lhm::SqlRetry.new( connection, retry_options: options[:retriable] || {}, @@ -18,11 +21,6 @@ def initialize(connection:, default_log_prefix: nil, options: {}) super(connection) end - def ar_connection - # Get object from the simple delegator - __getobj__ - end - def ar_connection=(connection) raise Lhm::Error.new("Lhm::Connection requires an active record connection to operate") if connection.nil? @@ -31,46 +29,53 @@ def ar_connection=(connection) __setobj__(connection) end - def process_connection_options(options) - # If any other flags are added. Add the "processing" here - @sql_retry.reconnect_with_consistent_host = options[:reconnect_with_consistent_host] || false + # Accessors for SQLRetry options + def reconnect_with_consistent_host=(reconnect) + @sql_retry.reconnect_with_consistent_host = reconnect + end + + def retry_config=(config) + @retry_config = config + @sql_retry.retry_config = config end - def execute(query, should_retry: false, retry_options: {}) + # ActiveRecord::Base overridden methods to incorporate custom retry logic + # All other methods will be delegated + def execute(query, should_retry: false, log_prefix: nil) if should_retry - exec_with_retries(:execute, query, retry_options) + exec_with_retries(:execute, query, log_prefix) else exec(:execute, query) end end - def update(query, should_retry: false, retry_options: {}) + def update(query, should_retry: false, log_prefix: nil) if should_retry - exec_with_retries(:update, query, retry_options) + exec_with_retries(:update, query, log_prefix) else exec(:update, query) end end - def select_value(query, should_retry: false, retry_options: {}) + def select_value(query, should_retry: false, log_prefix: nil) if should_retry - exec_with_retries(:select_value, query, retry_options) + exec_with_retries(:select_value, query, log_prefix) else exec(:select_value, query) end end - def select_values(query, should_retry: false, retry_options: {}) + def select_values(query, should_retry: false, log_prefix: nil) if should_retry - exec_with_retries(:select_values, query, retry_options) + exec_with_retries(:select_values, query, log_prefix) else exec(:select_values, query) end end - def select_one(query, should_retry: false, retry_options: {}) + def select_one(query, should_retry: false, log_prefix: nil) if should_retry - exec_with_retries(:select_one, query, retry_options) + exec_with_retries(:select_one, query, log_prefix) else exec(:select_one, query) end @@ -82,9 +87,9 @@ def exec(method, sql) ar_connection.public_send(method, Lhm::ProxySQLHelper.tagged(sql)) end - def exec_with_retries(method, sql, retry_options = {}) - retry_options[:log_prefix] ||= file - @sql_retry.with_retries(retry_options) do |conn| + def exec_with_retries(method, sql, log_prefix=nil) + effective_log_prefix = log_prefix || file + @sql_retry.with_retries(log_prefix: effective_log_prefix) do |conn| conn.public_send(method, Lhm::ProxySQLHelper.tagged(sql)) end end diff --git a/lib/lhm/entangler.rb b/lib/lhm/entangler.rb index 06f427ce..60854629 100644 --- a/lib/lhm/entangler.rb +++ b/lib/lhm/entangler.rb @@ -13,14 +13,15 @@ class Entangler attr_reader :connection + LOG_PREFIX = "Entangler" + # Creates entanglement between two tables. All creates, updates and deletes # to origin will be repeated on the destination table. - def initialize(migration, connection = nil, options = {}) + def initialize(migration, connection = nil) @intersection = migration.intersection @origin = migration.origin @destination = migration.destination @connection = connection - @retry_options = options[:retriable] || {} end def entangle @@ -86,14 +87,14 @@ def validate def before entangle.each do |stmt| - @connection.execute(stmt, should_retry: true, retry_options: @retry_options) + @connection.execute(stmt, should_retry: true, log_prefix: LOG_PREFIX) end Lhm.logger.info("Created triggers on #{@origin.name}") end def after untangle.each do |stmt| - @connection.execute(stmt, should_retry: true, retry_options: @retry_options) + @connection.execute(stmt, should_retry: true, log_prefix: LOG_PREFIX) end Lhm.logger.info("Dropped triggers on #{@origin.name}") end diff --git a/lib/lhm/invoker.rb b/lib/lhm/invoker.rb index 21e79bfb..d0c602bd 100644 --- a/lib/lhm/invoker.rb +++ b/lib/lhm/invoker.rb @@ -16,8 +16,8 @@ module Lhm class Invoker include SqlHelper LOCK_WAIT_TIMEOUT_DELTA = 10 - INNODB_LOCK_WAIT_TIMEOUT_MAX=1073741824.freeze # https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout - LOCK_WAIT_TIMEOUT_MAX=31536000.freeze # https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html + INNODB_LOCK_WAIT_TIMEOUT_MAX = 1073741824.freeze # https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_lock_wait_timeout + LOCK_WAIT_TIMEOUT_MAX = 31536000.freeze # https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html attr_reader :migrator, :connection @@ -49,7 +49,7 @@ def run(options = {}) normalize_options(options) set_session_lock_wait_timeouts migration = @migrator.run - entangler = Entangler.new(migration, @connection, options) + entangler = Entangler.new(migration, @connection) entangler.run do options[:verifier] ||= Proc.new { |conn| triggers_still_exist?(conn, entangler) } @@ -90,6 +90,8 @@ def normalize_options(options) options[:throttler] = Lhm.throttler end + Lhm.connection.retry_config = options[:retriable] || {} + rescue => e Lhm.logger.error "LHM run failed with exception=#{e.class} message=#{e.message}" raise diff --git a/lib/lhm/locked_switcher.rb b/lib/lhm/locked_switcher.rb index 13e39d8d..da4c725c 100644 --- a/lib/lhm/locked_switcher.rb +++ b/lib/lhm/locked_switcher.rb @@ -22,6 +22,8 @@ class LockedSwitcher attr_reader :connection + LOG_PREFIX = "LockedSwitcher" + def initialize(migration, connection = nil) @migration = migration @connection = connection diff --git a/lib/lhm/proxysql_helper.rb b/lib/lhm/proxysql_helper.rb index f4e05872..9626a9fd 100644 --- a/lib/lhm/proxysql_helper.rb +++ b/lib/lhm/proxysql_helper.rb @@ -3,8 +3,16 @@ module ProxySQLHelper extend self ANNOTATION = "/*maintenance:lhm*/" + attr_writer :disable_tags + + # Default value + def disable_tags + return false unless defined?(@disable_tags) + @disable_tags + end + def tagged(sql) - "#{sql} #{ANNOTATION}" + "#{ANNOTATION + " " unless disable_tags}#{sql}" end end end diff --git a/lib/lhm/sql_retry.rb b/lib/lhm/sql_retry.rb index 39bb2a3d..dcb89817 100644 --- a/lib/lhm/sql_retry.rb +++ b/lib/lhm/sql_retry.rb @@ -30,21 +30,15 @@ class ReconnectToHostSuccessful < Lhm::Error; end def initialize(connection, retry_options: {}, reconnect_with_consistent_host: false) @connection = connection - @log_prefix = retry_options.delete(:log_prefix) - @global_retry_config = default_retry_config.dup.merge!(retry_options) - if (@reconnect_with_consistent_host = reconnect_with_consistent_host) - @initial_hostname = hostname - @initial_server_id = server_id - end + self.retry_config = retry_options + self.reconnect_with_consistent_host = reconnect_with_consistent_host end # Complete explanation of algorithm: https://github.com/Shopify/lhm/pull/112 - def with_retries(retry_config = {}) - @log_prefix = retry_config.delete(:log_prefix) + def with_retries(log_prefix: nil) + @log_prefix = log_prefix || "" # No prefix. Just logs - retry_config = @global_retry_config.dup.merge!(retry_config) - - Retriable.retriable(retry_config) do + Retriable.retriable(@retry_config) do # Using begin -> rescue -> end for Ruby 2.4 compatibility begin if @reconnect_with_consistent_host @@ -62,8 +56,20 @@ def with_retries(retry_config = {}) end end - attr_reader :global_retry_config - attr_accessor :connection, :reconnect_with_consistent_host + # Both attributes will have defined setters + attr_reader :retry_config, :reconnect_with_consistent_host + attr_accessor :connection + + def retry_config=(retry_options) + @retry_config = default_retry_config.dup.merge!(retry_options) + end + + def reconnect_with_consistent_host=(reconnect) + if (@reconnect_with_consistent_host = reconnect) + @initial_hostname = hostname + @initial_server_id = server_id + end + end private diff --git a/lib/lhm/version.rb b/lib/lhm/version.rb index 9e0f43ce..3a4bb7eb 100644 --- a/lib/lhm/version.rb +++ b/lib/lhm/version.rb @@ -2,5 +2,5 @@ # Schmidt module Lhm - VERSION = '3.5.3' + VERSION = '3.5.4' end diff --git a/spec/integration/atomic_switcher_spec.rb b/spec/integration/atomic_switcher_spec.rb index 4eb46398..2b628f01 100644 --- a/spec/integration/atomic_switcher_spec.rb +++ b/spec/integration/atomic_switcher_spec.rb @@ -39,10 +39,15 @@ .then .returns([["dummy"]]) # Matches initial host -> triggers retry - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + tries: 3, + base_interval: 0 + } + }) - - switcher = Lhm::AtomicSwitcher.new(@migration, connection, retriable: { tries: 3, base_interval: 0 }) + switcher = Lhm::AtomicSwitcher.new(@migration, connection) assert switcher.run @@ -58,20 +63,26 @@ ar_connection.stubs(:data_source_exists?).returns(true) ar_connection.stubs(:active?).returns(true) ar_connection.stubs(:execute).returns([["dummy"]], [["dummy"]], [["dummy"]]) - .then - .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') - .then - .returns([["dummy"]]) # triggers retry 1 - .then - .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') - .then - .returns([["dummy"]]) # triggers retry 2 - .then - .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') # triggers retry 2 - - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) - - switcher = Lhm::AtomicSwitcher.new(@migration, connection, retriable: { tries: 2, base_interval: 0 }) + .then + .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') + .then + .returns([["dummy"]]) # triggers retry 1 + .then + .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') + .then + .returns([["dummy"]]) # triggers retry 2 + .then + .raises(ActiveRecord::StatementInvalid, 'Lock wait timeout exceeded; try restarting transaction.') # triggers retry 2 + + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + tries: 2, + base_interval: 0 + } + }) + + switcher = Lhm::AtomicSwitcher.new(@migration, connection) assert_raises(ActiveRecord::StatementInvalid) { switcher.run } end diff --git a/spec/integration/chunker_spec.rb b/spec/integration/chunker_spec.rb index 1f7b0a58..d282e5b0 100644 --- a/spec/integration/chunker_spec.rb +++ b/spec/integration/chunker_spec.rb @@ -173,6 +173,9 @@ def log_messages printer.expect(:notify, :return_value, [Integer, Integer]) printer.expect(:end, :return_value, []) + Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1']) + Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1']) + Lhm::Chunker.new( @migration, connection, { throttler: Lhm::Throttler::SlaveLag.new(stride: 100), printer: printer } ).run @@ -215,17 +218,16 @@ def throttler.max_current_slave_lag printer.expects(:verify) printer.expects(:end) - throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0) + Lhm::Throttler::Slave.any_instance.stubs(:slave_hosts).returns(['127.0.0.1']) + Lhm::Throttler::SlaveLag.any_instance.stubs(:master_slave_hosts).returns(['127.0.0.1']) - def throttler.slave_hosts - ['127.0.0.1'] - end + throttler = Lhm::Throttler::SlaveLag.new(stride: 10, allowed_lag: 0) if master_slave_mode? def throttler.slave_connection(slave) config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup config[:host] = slave - config[:port] = 3307 + config[:port] = 33007 ActiveRecord::Base.send('mysql2_connection', config) end end diff --git a/spec/integration/integration_helper.rb b/spec/integration/integration_helper.rb index 10b69087..be45b27e 100644 --- a/spec/integration/integration_helper.rb +++ b/spec/integration/integration_helper.rb @@ -62,18 +62,16 @@ def connect_slave! ) end - def connect_master_with_toxiproxy!(with_retry: false) + def connect_master_with_toxiproxy! connect!( '127.0.0.1', $db_config['master_toxic']['port'], $db_config['master_toxic']['user'], - $db_config['master_toxic']['password'], - with_retry) + $db_config['master_toxic']['password']) end - def connect!(hostname, port, user, password, with_retry = false) - adapter = ar_conn(hostname, port, user, password) - Lhm.setup(adapter,{reconnect_with_consistent_host: with_retry}) + def connect!(hostname, port, user, password) + Lhm.setup(ar_conn(hostname, port, user, password)) unless defined?(@@cleaned_up) Lhm.cleanup(true) @@cleaned_up = true diff --git a/spec/integration/lhm_spec.rb b/spec/integration/lhm_spec.rb index 7247b600..5fd49830 100644 --- a/spec/integration/lhm_spec.rb +++ b/spec/integration/lhm_spec.rb @@ -592,7 +592,7 @@ end it " should not try to reconnect if reconnect_with_consistent_host is not provided" do - connect_master_with_toxiproxy!(with_retry: false) + connect_master_with_toxiproxy! table_create(:users) 100.times { |n| execute("insert into users set reference = '#{ n }'") } @@ -610,7 +610,7 @@ end it "should reconnect if reconnect_with_consistent_host is true" do - connect_master_with_toxiproxy!(with_retry: true) + connect_master_with_toxiproxy! mysql_disabled = false table_create(:users) @@ -635,7 +635,7 @@ method_added(:insert_and_return_count_of_rows_created) end - Lhm.change_table(:users, :atomic_switch => false) do |t| + Lhm.change_table(:users, atomic_switch: false, reconnect_with_consistent_host: true) do |t| t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL") t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)") t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)") @@ -655,6 +655,21 @@ value(count_all(:users)).must_equal(100) end end + + it "should not tag the queries with ProxySQL's tags if requested" do + ActiveRecord::Base.logger = Logger.new(@logs) + + Lhm.change_table(:users, atomic_switch: false, disable_proxysql_tags: true) do |t| + t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL") + t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)") + t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)") + t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT") + end + + log_lines = @logs.string.split("\n") + + assert log_lines.none?{ |line| line.include?("/*maintenance:lhm*/")} + end end end end diff --git a/spec/unit/chunker_spec.rb b/spec/unit/chunker_spec.rb index 407cd214..2b62836e 100644 --- a/spec/unit/chunker_spec.rb +++ b/spec/unit/chunker_spec.rb @@ -12,7 +12,8 @@ describe Lhm::Chunker do include UnitHelper - EXPECTED_RETRY_FLAGS = {:should_retry => true, :retry_options => {}} + EXPECTED_RETRY_FLAGS_CHUNKER = {:should_retry => true, :log_prefix => "Chunker"} + EXPECTED_RETRY_FLAGS_CHUNK_INSERT = {:should_retry => true, :log_prefix => "ChunkInsert"} before(:each) do @origin = Lhm::Table.new('foo') @@ -41,11 +42,11 @@ def @throttler.stride 5 end - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS).returns(7) - @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS).returns(21) - @connection.expects(:update).with(regexp_matches(/between 1 and 7/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 8 and 10/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:execute).twice.with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS).returns([]) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(7) + @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(21) + @connection.expects(:update).with(regexp_matches(/between 1 and 7/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 8 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:execute).twice.with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([]) @chunker.run end @@ -56,17 +57,17 @@ def @throttler.stride 2 end - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(4) - @connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(6) - @connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(8) - @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(10) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2) + @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(4) + @connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(6) + @connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(8) + @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(10) - @connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 3 and 4/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 5 and 6/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 7 and 8/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS).returns(2) + @connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 3 and 4/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 5 and 6/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 7 and 8/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) @chunker.run end @@ -83,17 +84,17 @@ def @throttler.stride end end - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS).returns(5) - @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS).returns(8) - @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS).returns(nil) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2) + @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(5) + @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(8) + @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil) - @connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 3 and 5/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 6 and 8/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS).returns(2) + @connection.expects(:update).with(regexp_matches(/between 1 and 2/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 3 and 5/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 6 and 8/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 9 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) - @connection.expects(:execute).twice.with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS).returns([]) + @connection.expects(:execute).twice.with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([]) @chunker.run end @@ -103,8 +104,8 @@ def @throttler.stride :start => 1, :limit => 1) - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/),EXPECTED_RETRY_FLAGS).returns(nil) - @connection.expects(:update).with(regexp_matches(/between 1 and 1/),EXPECTED_RETRY_FLAGS).returns(1) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil) + @connection.expects(:update).with(regexp_matches(/between 1 and 1/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1) @chunker.run end @@ -117,17 +118,17 @@ def @throttler.stride 2 end - @connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(3) - @connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(5) - @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(7) - @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(9) - @connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(nil) + @connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(3) + @connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(5) + @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(7) + @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(9) + @connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(nil) - @connection.expects(:update).with(regexp_matches(/between 2 and 3/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 4 and 5/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 6 and 7/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 8 and 9/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/between 10 and 10/),EXPECTED_RETRY_FLAGS).returns(1) + @connection.expects(:update).with(regexp_matches(/between 2 and 3/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 4 and 5/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 6 and 7/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 8 and 9/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(2) + @connection.expects(:update).with(regexp_matches(/between 10 and 10/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1) @chunker.run end @@ -141,9 +142,9 @@ def @throttler.stride 2 end - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/),EXPECTED_RETRY_FLAGS).returns(1) - @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS).returns([]) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2) + @connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1) + @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([]) def @migration.conditions "where foo.created_at > '2013-07-10' or foo.baz = 'quux'" @@ -161,9 +162,9 @@ def @throttler.stride 2 end - @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS).returns(2) - @connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/),EXPECTED_RETRY_FLAGS).returns(1) - @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS).returns([]) + @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/),EXPECTED_RETRY_FLAGS_CHUNKER).returns(2) + @connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/),EXPECTED_RETRY_FLAGS_CHUNK_INSERT).returns(1) + @connection.expects(:execute).with(regexp_matches(/show warnings/),EXPECTED_RETRY_FLAGS_CHUNKER).returns([]) def @migration.conditions 'inner join bar on foo.id = bar.foo_id' diff --git a/spec/unit/connection_spec.rb b/spec/unit/connection_spec.rb index 34078bbc..04233244 100644 --- a/spec/unit/connection_spec.rb +++ b/spec/unit/connection_spec.rb @@ -15,9 +15,13 @@ ar_connection.stubs(:execute).raises(LOCK_WAIT).then.returns(true) ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection) + connection = Lhm::Connection.new(connection: ar_connection, options: { + retriable: { + base_interval: 0 + } + }) - connection.execute("SHOW TABLES", should_retry: true, retry_options: { base_interval: 0 }) + connection.execute("SHOW TABLES", should_retry: true) log_messages = @logs.string.split("\n") assert_equal(1, log_messages.length) @@ -31,9 +35,14 @@ .then.returns(true) ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection) + connection = Lhm::Connection.new(connection: ar_connection, options: { + retriable: { + base_interval: 0, + tries: 3 + } + }) - connection.execute("SHOW TABLES", should_retry: true, retry_options: { base_interval: 0, tries: 3 }) + connection.execute("SHOW TABLES", should_retry: true) log_messages = @logs.string.split("\n") assert_equal(2, log_messages.length) @@ -46,9 +55,14 @@ .then.returns(1) ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection) + connection = Lhm::Connection.new(connection: ar_connection, options: { + retriable: { + base_interval: 0, + tries: 3 + } + }) - val = connection.update("SHOW TABLES", should_retry: true, retry_options:{ base_interval: 0, tries: 3 }) + val = connection.update("SHOW TABLES", should_retry: true) log_messages = @logs.string.split("\n") assert_equal val, 1 @@ -62,24 +76,35 @@ .then.returns("dummy") ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection) + connection = Lhm::Connection.new(connection: ar_connection, options: { + retriable: { + base_interval: 0, + tries: 3 + } + }) - val = connection.select_value("SHOW TABLES", should_retry: true, retry_options: { base_interval: 0, tries: 3 }) + val = connection.select_value("SHOW TABLES", should_retry: true) log_messages = @logs.string.split("\n") assert_equal val, "dummy" assert_equal(2, log_messages.length) end - it "Queries should be tagged with ProxySQL tag if requested" do + it "Queries should be tagged with ProxySQL tag if reconnect_with_consistent_host is enabled" do ar_connection = mock() - ar_connection.expects(:public_send).with(:select_value, "SHOW TABLES #{Lhm::ProxySQLHelper::ANNOTATION}").returns("dummy") + ar_connection.expects(:public_send).with(:select_value, "#{Lhm::ProxySQLHelper::ANNOTATION} SHOW TABLES").returns("dummy") ar_connection.stubs(:execute).times(4).returns([["dummy"]]) ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection, options: { reconnect_with_consistent_host: true }) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + base_interval: 0, + tries: 3 + } + }) - val = connection.select_value("SHOW TABLES", should_retry: true, retry_options: { base_interval: 0, tries: 3 }) + val = connection.select_value("SHOW TABLES", should_retry: true) assert_equal val, "dummy" end diff --git a/spec/unit/entangler_spec.rb b/spec/unit/entangler_spec.rb index 8a8e33b5..f851a150 100644 --- a/spec/unit/entangler_spec.rb +++ b/spec/unit/entangler_spec.rb @@ -69,9 +69,15 @@ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction') ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + base_interval: 0, + tries: tries + } + }) - @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0, tries: tries}) + @entangler = Lhm::Entangler.new(@migration, connection) assert_raises(Mysql2::Error) { @entangler.before } end @@ -83,9 +89,14 @@ .then .raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.') ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) - - @entangler = Lhm::Entangler.new(@migration, connection, retriable: { base_interval: 0 }) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + base_interval: 0 + }, + }) + + @entangler = Lhm::Entangler.new(@migration, connection) assert_raises(Mysql2::Error) { @entangler.before } end @@ -99,9 +110,14 @@ .returns([["dummy"]]) ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + base_interval: 0 + }, + }) - @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0}) + @entangler = Lhm::Entangler.new(@migration, connection) assert @entangler.before end @@ -126,9 +142,15 @@ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction') # final error ar_connection.stubs(:active?).returns(true) - connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: true}) + connection = Lhm::Connection.new(connection: ar_connection, options: { + reconnect_with_consistent_host: true, + retriable: { + tries: 5, + base_interval: 0 + }, + }) - @entangler = Lhm::Entangler.new(@migration, connection, retriable: {tries: 5, base_interval: 0}) + @entangler = Lhm::Entangler.new(@migration, connection) assert_raises(Mysql2::Error) { @entangler.before } end diff --git a/spec/unit/proxysql_helper_spec.rb b/spec/unit/proxysql_helper_spec.rb new file mode 100644 index 00000000..9eef0c33 --- /dev/null +++ b/spec/unit/proxysql_helper_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path(File.dirname(__FILE__)) + '/unit_helper' + +describe Lhm::ProxySQLHelper do + + after(:each) do + Lhm::ProxySQLHelper.disable_tags = false + end + + it "should not tag if the flag is active" do + Lhm::ProxySQLHelper.disable_tags = true + assert_equal("dummy", Lhm::ProxySQLHelper.tagged("dummy")) + end + + it "should tag if the flag is not active (default)" do + assert_equal("/*maintenance:lhm*/ dummy", Lhm::ProxySQLHelper.tagged("dummy")) + end +end \ No newline at end of file diff --git a/spec/unit/throttler/slave_lag_spec.rb b/spec/unit/throttler/slave_lag_spec.rb index 542855be..db34ccc1 100644 --- a/spec/unit/throttler/slave_lag_spec.rb +++ b/spec/unit/throttler/slave_lag_spec.rb @@ -142,7 +142,7 @@ def self.query(query) Lhm::Throttler::Slave.any_instance.stubs(:config).returns([]) slave = Lhm::Throttler::Slave.new('slave', @dummy_mysql_client_config) - assert_send([Lhm.logger, :info, "Unable to connect and/or query slave: error"]) + Logger.any_instance.expects(:info).with("Unable to connect and/or query slave: Can't connect to MySQL server") assert_equal(0, slave.lag) end end