lhm-shopify 3.5.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +6 -6
- data/Appraisals +8 -13
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +22 -20
- data/README.md +14 -7
- data/dev.yml +12 -8
- data/docker-compose.yml +2 -0
- data/gemfiles/activerecord_6.0.gemfile +1 -1
- data/gemfiles/activerecord_6.0.gemfile.lock +25 -21
- data/gemfiles/activerecord_6.1.gemfile.lock +17 -13
- data/gemfiles/{activerecord_7.0.0.alpha2.gemfile → activerecord_7.0.gemfile} +1 -1
- data/gemfiles/{activerecord_7.0.0.alpha2.gemfile.lock → activerecord_7.0.gemfile.lock} +23 -19
- data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.1.0.beta1.gemfile} +1 -3
- data/gemfiles/activerecord_7.1.0.beta1.gemfile.lock +81 -0
- data/lhm.gemspec +1 -1
- data/lib/lhm/sql_helper.rb +1 -1
- data/lib/lhm/sql_retry.rb +37 -47
- data/lib/lhm/throttler/replica_lag.rb +162 -0
- data/lib/lhm/throttler/slave_lag.rb +5 -155
- data/lib/lhm/throttler/threads_running.rb +3 -1
- data/lib/lhm/throttler.rb +7 -3
- data/lib/lhm/version.rb +1 -1
- data/spec/.lhm.example +1 -1
- data/spec/README.md +8 -9
- data/spec/integration/atomic_switcher_spec.rb +2 -2
- data/spec/integration/chunk_insert_spec.rb +2 -2
- data/spec/integration/chunker_spec.rb +33 -38
- data/spec/integration/database.yml +1 -1
- data/spec/integration/entangler_spec.rb +4 -4
- data/spec/integration/integration_helper.rb +12 -12
- data/spec/integration/lhm_spec.rb +41 -32
- data/spec/integration/locked_switcher_spec.rb +2 -2
- data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +6 -5
- data/spec/integration/toxiproxy_helper.rb +1 -1
- data/spec/test_helper.rb +3 -0
- data/spec/unit/printer_spec.rb +2 -6
- data/spec/unit/sql_helper_spec.rb +2 -2
- data/spec/unit/throttler/{slave_lag_spec.rb → replica_lag_spec.rb} +79 -79
- data/spec/unit/throttler/threads_running_spec.rb +18 -0
- data/spec/unit/throttler_spec.rb +8 -8
- metadata +10 -9
- data/gemfiles/activerecord_5.2.gemfile.lock +0 -65
| @@ -28,7 +28,7 @@ describe Lhm::Chunker do | |
| 28 28 |  | 
| 29 29 | 
             
                  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
         | 
| 30 30 |  | 
| 31 | 
            -
                   | 
| 31 | 
            +
                  replica do
         | 
| 32 32 | 
             
                    value(count_all(@destination.name)).must_equal(1)
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 |  | 
| @@ -41,7 +41,7 @@ describe Lhm::Chunker do | |
| 41 41 |  | 
| 42 42 | 
             
                  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
         | 
| 43 43 |  | 
| 44 | 
            -
                   | 
| 44 | 
            +
                  replica do
         | 
| 45 45 | 
             
                    value(count_all(@destination.name)).must_equal(2)
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 | 
             
                end
         | 
| @@ -57,7 +57,7 @@ describe Lhm::Chunker do | |
| 57 57 |  | 
| 58 58 | 
             
                  Lhm::Chunker.new(migration, connection, {throttler: throttler, printer: printer} ).run
         | 
| 59 59 |  | 
| 60 | 
            -
                   | 
| 60 | 
            +
                  replica do
         | 
| 61 61 | 
             
                    value(count_all(destination.name)).must_equal(2)
         | 
| 62 62 | 
             
                  end
         | 
| 63 63 | 
             
                end
         | 
| @@ -127,7 +127,7 @@ describe Lhm::Chunker do | |
| 127 127 |  | 
| 128 128 | 
             
                  Lhm::Chunker.new(@migration, connection, {throttler: throttler, printer: printer} ).run
         | 
| 129 129 |  | 
| 130 | 
            -
                   | 
| 130 | 
            +
                  replica do
         | 
| 131 131 | 
             
                    value(count_all(@destination.name)).must_equal(0)
         | 
| 132 132 | 
             
                  end
         | 
| 133 133 |  | 
| @@ -136,20 +136,17 @@ describe Lhm::Chunker do | |
| 136 136 | 
             
                it 'should copy 23 rows from origin to destination in one shot, regardless of the value of the id' do
         | 
| 137 137 | 
             
                  23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
         | 
| 138 138 |  | 
| 139 | 
            -
                  printer =  | 
| 140 | 
            -
                  printer. | 
| 141 | 
            -
                  printer. | 
| 139 | 
            +
                  printer = mock("printer")
         | 
| 140 | 
            +
                  printer.expects(:notify).with(kind_of(Integer), kind_of(Integer))
         | 
| 141 | 
            +
                  printer.expects(:end)
         | 
| 142 142 |  | 
| 143 143 | 
             
                  Lhm::Chunker.new(
         | 
| 144 144 | 
             
                    @migration, connection, { throttler: throttler, printer: printer }
         | 
| 145 145 | 
             
                  ).run
         | 
| 146 146 |  | 
| 147 | 
            -
                   | 
| 147 | 
            +
                  replica do
         | 
| 148 148 | 
             
                    value(count_all(@destination.name)).must_equal(23)
         | 
| 149 149 | 
             
                  end
         | 
| 150 | 
            -
             | 
| 151 | 
            -
                  printer.verify
         | 
| 152 | 
            -
             | 
| 153 150 | 
             
                end
         | 
| 154 151 |  | 
| 155 152 | 
             
                it 'should copy all the records of a table, even if the last chunk starts with the last record of it.' do
         | 
| @@ -160,42 +157,40 @@ describe Lhm::Chunker do | |
| 160 157 | 
             
                    @migration, connection, { throttler: Lhm::Throttler::Time.new(stride: 10), printer: printer }
         | 
| 161 158 | 
             
                  ).run
         | 
| 162 159 |  | 
| 163 | 
            -
                   | 
| 160 | 
            +
                  replica do
         | 
| 164 161 | 
             
                    value(count_all(@destination.name)).must_equal(11)
         | 
| 165 162 | 
             
                  end
         | 
| 166 163 |  | 
| 167 164 | 
             
                end
         | 
| 168 165 |  | 
| 169 | 
            -
                it 'should copy 23 rows from origin to destination in one shot with  | 
| 166 | 
            +
                it 'should copy 23 rows from origin to destination in one shot with replica lag based throttler, regardless of the value of the id' do
         | 
| 170 167 | 
             
                  23.times { |n| execute("insert into origin set id = '#{ 100000 + n * n + 23 }'") }
         | 
| 171 168 |  | 
| 172 | 
            -
                  printer =  | 
| 173 | 
            -
                  printer. | 
| 174 | 
            -
                  printer. | 
| 169 | 
            +
                  printer = mock("printer")
         | 
| 170 | 
            +
                  printer.expects(:notify).with(kind_of(Integer), kind_of(Integer))
         | 
| 171 | 
            +
                  printer.expects(:end)
         | 
| 175 172 |  | 
| 176 | 
            -
                  Lhm::Throttler:: | 
| 177 | 
            -
                  Lhm::Throttler:: | 
| 173 | 
            +
                  Lhm::Throttler::Replica.any_instance.stubs(:replica_hosts).returns(['127.0.0.1'])
         | 
| 174 | 
            +
                  Lhm::Throttler::ReplicaLag.any_instance.stubs(:master_replica_hosts).returns(['127.0.0.1'])
         | 
| 178 175 |  | 
| 179 176 | 
             
                  Lhm::Chunker.new(
         | 
| 180 | 
            -
                    @migration, connection, { throttler: Lhm::Throttler:: | 
| 177 | 
            +
                    @migration, connection, { throttler: Lhm::Throttler::ReplicaLag.new(stride: 100), printer: printer }
         | 
| 181 178 | 
             
                  ).run
         | 
| 182 179 |  | 
| 183 | 
            -
                   | 
| 180 | 
            +
                  replica do
         | 
| 184 181 | 
             
                    value(count_all(@destination.name)).must_equal(23)
         | 
| 185 182 | 
             
                  end
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                  printer.verify
         | 
| 188 183 | 
             
                end
         | 
| 189 184 |  | 
| 190 | 
            -
                it 'should throttle work stride based on  | 
| 185 | 
            +
                it 'should throttle work stride based on replica lag' do
         | 
| 191 186 | 
             
                  15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
         | 
| 192 187 |  | 
| 193 188 | 
             
                  printer = mock()
         | 
| 194 189 | 
             
                  printer.expects(:notify).with(instance_of(Integer), instance_of(Integer)).twice
         | 
| 195 190 | 
             
                  printer.expects(:end)
         | 
| 196 191 |  | 
| 197 | 
            -
                  throttler = Lhm::Throttler:: | 
| 198 | 
            -
                  def throttler. | 
| 192 | 
            +
                  throttler = Lhm::Throttler::ReplicaLag.new(stride: 10, allowed_lag: 0)
         | 
| 193 | 
            +
                  def throttler.max_current_replica_lag
         | 
| 199 194 | 
             
                    1
         | 
| 200 195 | 
             
                  end
         | 
| 201 196 |  | 
| @@ -203,14 +198,14 @@ describe Lhm::Chunker do | |
| 203 198 | 
             
                    @migration, connection, { throttler: throttler, printer: printer }
         | 
| 204 199 | 
             
                  ).run
         | 
| 205 200 |  | 
| 206 | 
            -
                  assert_equal(Lhm::Throttler:: | 
| 201 | 
            +
                  assert_equal(Lhm::Throttler::ReplicaLag::INITIAL_TIMEOUT * 2 * 2, throttler.timeout_seconds)
         | 
| 207 202 |  | 
| 208 | 
            -
                   | 
| 203 | 
            +
                  replica do
         | 
| 209 204 | 
             
                    value(count_all(@destination.name)).must_equal(15)
         | 
| 210 205 | 
             
                  end
         | 
| 211 206 | 
             
                end
         | 
| 212 207 |  | 
| 213 | 
            -
                it 'should detect a single  | 
| 208 | 
            +
                it 'should detect a single replica with no lag in the default configuration' do
         | 
| 214 209 | 
             
                  15.times { |n| execute("insert into origin set id = '#{ (n * n) + 1 }'") }
         | 
| 215 210 |  | 
| 216 211 | 
             
                  printer = mock()
         | 
| @@ -218,15 +213,15 @@ describe Lhm::Chunker do | |
| 218 213 | 
             
                  printer.expects(:verify)
         | 
| 219 214 | 
             
                  printer.expects(:end)
         | 
| 220 215 |  | 
| 221 | 
            -
                  Lhm::Throttler:: | 
| 222 | 
            -
                  Lhm::Throttler:: | 
| 216 | 
            +
                  Lhm::Throttler::Replica.any_instance.stubs(:replica_hosts).returns(['127.0.0.1'])
         | 
| 217 | 
            +
                  Lhm::Throttler::ReplicaLag.any_instance.stubs(:master_replica_hosts).returns(['127.0.0.1'])
         | 
| 223 218 |  | 
| 224 | 
            -
                  throttler = Lhm::Throttler:: | 
| 219 | 
            +
                  throttler = Lhm::Throttler::ReplicaLag.new(stride: 10, allowed_lag: 0)
         | 
| 225 220 |  | 
| 226 | 
            -
                  if  | 
| 227 | 
            -
                    def throttler. | 
| 221 | 
            +
                  if master_replica_mode?
         | 
| 222 | 
            +
                    def throttler.replica_connection(replica)
         | 
| 228 223 | 
             
                      config = ActiveRecord::Base.connection_pool.db_config.configuration_hash.dup
         | 
| 229 | 
            -
                      config[:host] =  | 
| 224 | 
            +
                      config[:host] = replica
         | 
| 230 225 | 
             
                      config[:port] = 33007
         | 
| 231 226 | 
             
                      ActiveRecord::Base.send('mysql2_connection', config)
         | 
| 232 227 | 
             
                    end
         | 
| @@ -236,10 +231,10 @@ describe Lhm::Chunker do | |
| 236 231 | 
             
                    @migration, connection, { throttler: throttler, printer: printer }
         | 
| 237 232 | 
             
                  ).run
         | 
| 238 233 |  | 
| 239 | 
            -
                  assert_equal(Lhm::Throttler:: | 
| 240 | 
            -
                  assert_equal(0, throttler.send(: | 
| 234 | 
            +
                  assert_equal(Lhm::Throttler::ReplicaLag::INITIAL_TIMEOUT, throttler.timeout_seconds)
         | 
| 235 | 
            +
                  assert_equal(0, throttler.send(:max_current_replica_lag))
         | 
| 241 236 |  | 
| 242 | 
            -
                   | 
| 237 | 
            +
                  replica do
         | 
| 243 238 | 
             
                    value(count_all(@destination.name)).must_equal(15)
         | 
| 244 239 | 
             
                  end
         | 
| 245 240 |  | 
| @@ -261,7 +256,7 @@ describe Lhm::Chunker do | |
| 261 256 |  | 
| 262 257 | 
             
                  assert_match "Verification failed, aborting early", exception.message
         | 
| 263 258 |  | 
| 264 | 
            -
                   | 
| 259 | 
            +
                  replica do
         | 
| 265 260 | 
             
                    value(count_all(@destination.name)).must_equal(0)
         | 
| 266 261 | 
             
                  end
         | 
| 267 262 | 
             
                end
         | 
| @@ -27,7 +27,7 @@ describe Lhm::Entangler do | |
| 27 27 | 
             
                    execute("insert into origin (common) values ('inserted')")
         | 
| 28 28 | 
             
                  end
         | 
| 29 29 |  | 
| 30 | 
            -
                   | 
| 30 | 
            +
                  replica do
         | 
| 31 31 | 
             
                    value(count(:destination, 'common', 'inserted')).must_equal(1)
         | 
| 32 32 | 
             
                  end
         | 
| 33 33 | 
             
                end
         | 
| @@ -39,7 +39,7 @@ describe Lhm::Entangler do | |
| 39 39 | 
             
                    execute("delete from origin where common = 'inserted'")
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 | 
            -
                   | 
| 42 | 
            +
                  replica do
         | 
| 43 43 | 
             
                    value(count(:destination, 'common', 'inserted')).must_equal(0)
         | 
| 44 44 | 
             
                  end
         | 
| 45 45 | 
             
                end
         | 
| @@ -50,7 +50,7 @@ describe Lhm::Entangler do | |
| 50 50 | 
             
                    execute("update origin set common = 'updated'")
         | 
| 51 51 | 
             
                  end
         | 
| 52 52 |  | 
| 53 | 
            -
                   | 
| 53 | 
            +
                  replica do
         | 
| 54 54 | 
             
                    value(count(:destination, 'common', 'updated')).must_equal(1)
         | 
| 55 55 | 
             
                  end
         | 
| 56 56 | 
             
                end
         | 
| @@ -60,7 +60,7 @@ describe Lhm::Entangler do | |
| 60 60 |  | 
| 61 61 | 
             
                  execute("insert into origin (common) values ('inserted')")
         | 
| 62 62 |  | 
| 63 | 
            -
                   | 
| 63 | 
            +
                  replica do
         | 
| 64 64 | 
             
                    value(count(:destination, 'common', 'inserted')).must_equal(0)
         | 
| 65 65 | 
             
                  end
         | 
| 66 66 | 
             
                end
         | 
| @@ -53,12 +53,12 @@ module IntegrationHelper | |
| 53 53 | 
             
                )
         | 
| 54 54 | 
             
              end
         | 
| 55 55 |  | 
| 56 | 
            -
              def  | 
| 56 | 
            +
              def connect_replica!
         | 
| 57 57 | 
             
                connect!(
         | 
| 58 58 | 
             
                  '127.0.0.1',
         | 
| 59 | 
            -
                  $db_config[' | 
| 60 | 
            -
                  $db_config[' | 
| 61 | 
            -
                  $db_config[' | 
| 59 | 
            +
                  $db_config['replica']['port'],
         | 
| 60 | 
            +
                  $db_config['replica']['user'],
         | 
| 61 | 
            +
                  $db_config['replica']['password'],
         | 
| 62 62 | 
             
                )
         | 
| 63 63 | 
             
              end
         | 
| 64 64 |  | 
| @@ -113,12 +113,12 @@ module IntegrationHelper | |
| 113 113 | 
             
                end
         | 
| 114 114 | 
             
              end
         | 
| 115 115 |  | 
| 116 | 
            -
              def  | 
| 117 | 
            -
                if  | 
| 118 | 
            -
                   | 
| 116 | 
            +
              def replica(&block)
         | 
| 117 | 
            +
                if master_replica_mode?
         | 
| 118 | 
            +
                  connect_replica!
         | 
| 119 119 |  | 
| 120 | 
            -
                  # need to wait for the  | 
| 121 | 
            -
                  # check the master binlog position and wait for the  | 
| 120 | 
            +
                  # need to wait for the replica to catch up. a better method would be to
         | 
| 121 | 
            +
                  # check the master binlog position and wait for the replica to catch up
         | 
| 122 122 | 
             
                  # to that position.
         | 
| 123 123 | 
             
                  sleep 1
         | 
| 124 124 | 
             
                else
         | 
| @@ -127,7 +127,7 @@ module IntegrationHelper | |
| 127 127 |  | 
| 128 128 | 
             
                yield block
         | 
| 129 129 |  | 
| 130 | 
            -
                if  | 
| 130 | 
            +
                if master_replica_mode?
         | 
| 131 131 | 
             
                  connect_master!
         | 
| 132 132 | 
             
                end
         | 
| 133 133 | 
             
              end
         | 
| @@ -215,8 +215,8 @@ module IntegrationHelper | |
| 215 215 | 
             
              # Environment
         | 
| 216 216 | 
             
              #
         | 
| 217 217 |  | 
| 218 | 
            -
              def  | 
| 219 | 
            -
                !!ENV[' | 
| 218 | 
            +
              def master_replica_mode?
         | 
| 219 | 
            +
                !!ENV['MASTER_REPLICA']
         | 
| 220 220 | 
             
              end
         | 
| 221 221 |  | 
| 222 222 | 
             
              #
         | 
| @@ -17,7 +17,7 @@ describe Lhm do | |
| 17 17 | 
             
                    t.add_column(:logins, "int(12) default '0'")
         | 
| 18 18 | 
             
                  end
         | 
| 19 19 |  | 
| 20 | 
            -
                   | 
| 20 | 
            +
                  replica do
         | 
| 21 21 | 
             
                    value(table_read(:users).columns['logins']).must_equal({
         | 
| 22 22 | 
             
                      :type           => 'int(12)',
         | 
| 23 23 | 
             
                      :is_nullable    => 'YES',
         | 
| @@ -35,7 +35,7 @@ describe Lhm do | |
| 35 35 | 
             
                    t.add_column(:logins, "int(12) default '0'")
         | 
| 36 36 | 
             
                  end
         | 
| 37 37 |  | 
| 38 | 
            -
                   | 
| 38 | 
            +
                  replica do
         | 
| 39 39 | 
             
                    value(table_read(:custom_primary_key).columns['logins']).must_equal({
         | 
| 40 40 | 
             
                      :type           => 'int(12)',
         | 
| 41 41 | 
             
                      :is_nullable    => 'YES',
         | 
| @@ -53,7 +53,7 @@ describe Lhm do | |
| 53 53 | 
             
                    t.add_column(:logins, "int(12) default '0'")
         | 
| 54 54 | 
             
                  end
         | 
| 55 55 |  | 
| 56 | 
            -
                   | 
| 56 | 
            +
                  replica do
         | 
| 57 57 | 
             
                    value(table_read(:composite_primary_key).columns['logins']).must_equal({
         | 
| 58 58 | 
             
                      :type           => 'int(12)',
         | 
| 59 59 | 
             
                      :is_nullable    => 'YES',
         | 
| @@ -82,7 +82,7 @@ describe Lhm do | |
| 82 82 | 
             
                      t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT")
         | 
| 83 83 | 
             
                    end
         | 
| 84 84 |  | 
| 85 | 
            -
                     | 
| 85 | 
            +
                    replica do
         | 
| 86 86 | 
             
                      value(connection.primary_key('users')).must_equal(['username', 'id'])
         | 
| 87 87 | 
             
                    end
         | 
| 88 88 | 
             
                  end
         | 
| @@ -104,7 +104,7 @@ describe Lhm do | |
| 104 104 | 
             
                  describe 'when no additional data is inserted into the table' do
         | 
| 105 105 |  | 
| 106 106 | 
             
                    it 'migrates the existing data' do
         | 
| 107 | 
            -
                       | 
| 107 | 
            +
                      replica do
         | 
| 108 108 | 
             
                        value(count_all(:permissions)).must_equal(11)
         | 
| 109 109 | 
             
                      end
         | 
| 110 110 | 
             
                    end
         | 
| @@ -120,7 +120,7 @@ describe Lhm do | |
| 120 120 | 
             
                    end
         | 
| 121 121 |  | 
| 122 122 | 
             
                    it 'migrates all data' do
         | 
| 123 | 
            -
                       | 
| 123 | 
            +
                      replica do
         | 
| 124 124 | 
             
                        value(count_all(:permissions)).must_equal(13)
         | 
| 125 125 | 
             
                      end
         | 
| 126 126 | 
             
                    end
         | 
| @@ -132,7 +132,7 @@ describe Lhm do | |
| 132 132 | 
             
                    t.add_column(:logins, "INT(12) DEFAULT '0'")
         | 
| 133 133 | 
             
                  end
         | 
| 134 134 |  | 
| 135 | 
            -
                   | 
| 135 | 
            +
                  replica do
         | 
| 136 136 | 
             
                    value(table_read(:users).columns['logins']).must_equal({
         | 
| 137 137 | 
             
                      :type => 'int(12)',
         | 
| 138 138 | 
             
                      :is_nullable => 'YES',
         | 
| @@ -150,7 +150,7 @@ describe Lhm do | |
| 150 150 | 
             
                    t.add_column(:logins, "INT(12) DEFAULT '0'")
         | 
| 151 151 | 
             
                  end
         | 
| 152 152 |  | 
| 153 | 
            -
                   | 
| 153 | 
            +
                  replica do
         | 
| 154 154 | 
             
                    value(count_all(:users)).must_equal(23)
         | 
| 155 155 | 
             
                  end
         | 
| 156 156 | 
             
                end
         | 
| @@ -160,7 +160,7 @@ describe Lhm do | |
| 160 160 | 
             
                    t.remove_column(:comment)
         | 
| 161 161 | 
             
                  end
         | 
| 162 162 |  | 
| 163 | 
            -
                   | 
| 163 | 
            +
                  replica do
         | 
| 164 164 | 
             
                    assert_nil table_read(:users).columns['comment']
         | 
| 165 165 | 
             
                  end
         | 
| 166 166 | 
             
                end
         | 
| @@ -170,7 +170,7 @@ describe Lhm do | |
| 170 170 | 
             
                    t.add_index([:comment, :created_at])
         | 
| 171 171 | 
             
                  end
         | 
| 172 172 |  | 
| 173 | 
            -
                   | 
| 173 | 
            +
                  replica do
         | 
| 174 174 | 
             
                    value(index_on_columns?(:users, [:comment, :created_at])).must_equal(true)
         | 
| 175 175 | 
             
                  end
         | 
| 176 176 | 
             
                end
         | 
| @@ -180,7 +180,7 @@ describe Lhm do | |
| 180 180 | 
             
                    t.add_index([:comment, :created_at], :my_index_name)
         | 
| 181 181 | 
             
                  end
         | 
| 182 182 |  | 
| 183 | 
            -
                   | 
| 183 | 
            +
                  replica do
         | 
| 184 184 | 
             
                    value(index?(:users, :my_index_name)).must_equal(true)
         | 
| 185 185 | 
             
                  end
         | 
| 186 186 | 
             
                end
         | 
| @@ -190,7 +190,7 @@ describe Lhm do | |
| 190 190 | 
             
                    t.add_index(:group)
         | 
| 191 191 | 
             
                  end
         | 
| 192 192 |  | 
| 193 | 
            -
                   | 
| 193 | 
            +
                  replica do
         | 
| 194 194 | 
             
                    value(index_on_columns?(:users, :group)).must_equal(true)
         | 
| 195 195 | 
             
                  end
         | 
| 196 196 | 
             
                end
         | 
| @@ -200,7 +200,7 @@ describe Lhm do | |
| 200 200 | 
             
                    t.add_unique_index(:comment)
         | 
| 201 201 | 
             
                  end
         | 
| 202 202 |  | 
| 203 | 
            -
                   | 
| 203 | 
            +
                  replica do
         | 
| 204 204 | 
             
                    value(index_on_columns?(:users, :comment, :unique)).must_equal(true)
         | 
| 205 205 | 
             
                  end
         | 
| 206 206 | 
             
                end
         | 
| @@ -210,7 +210,7 @@ describe Lhm do | |
| 210 210 | 
             
                    t.remove_index([:username, :created_at])
         | 
| 211 211 | 
             
                  end
         | 
| 212 212 |  | 
| 213 | 
            -
                   | 
| 213 | 
            +
                  replica do
         | 
| 214 214 | 
             
                    value(index_on_columns?(:users, [:username, :created_at])).must_equal(false)
         | 
| 215 215 | 
             
                  end
         | 
| 216 216 | 
             
                end
         | 
| @@ -220,7 +220,7 @@ describe Lhm do | |
| 220 220 | 
             
                    t.remove_index([:username, :group])
         | 
| 221 221 | 
             
                  end
         | 
| 222 222 |  | 
| 223 | 
            -
                   | 
| 223 | 
            +
                  replica do
         | 
| 224 224 | 
             
                    value(index?(:users, :index_with_a_custom_name)).must_equal(false)
         | 
| 225 225 | 
             
                  end
         | 
| 226 226 | 
             
                end
         | 
| @@ -230,17 +230,27 @@ describe Lhm do | |
| 230 230 | 
             
                    t.remove_index(:irrelevant_column_name, :index_with_a_custom_name)
         | 
| 231 231 | 
             
                  end
         | 
| 232 232 |  | 
| 233 | 
            -
                   | 
| 233 | 
            +
                  replica do
         | 
| 234 234 | 
             
                    value(index?(:users, :index_with_a_custom_name)).must_equal(false)
         | 
| 235 235 | 
             
                  end
         | 
| 236 236 | 
             
                end
         | 
| 237 237 |  | 
| 238 | 
            +
                it 'should add an index with column sizes' do
         | 
| 239 | 
            +
                  Lhm.change_table(:users, :atomic_switch => false) do |t|
         | 
| 240 | 
            +
                    t.add_index(["username(6)", "group (10)", "comment     (10)"])
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                  replica do
         | 
| 244 | 
            +
                    value(index_on_columns?(:users, [:username, :group, :comment])).must_equal(true)
         | 
| 245 | 
            +
                  end
         | 
| 246 | 
            +
                end
         | 
| 247 | 
            +
             | 
| 238 248 | 
             
                it 'should apply a ddl statement' do
         | 
| 239 249 | 
             
                  Lhm.change_table(:users, :atomic_switch => false) do |t|
         | 
| 240 250 | 
             
                    t.ddl('alter table %s add column flag tinyint(1)' % t.name)
         | 
| 241 251 | 
             
                  end
         | 
| 242 252 |  | 
| 243 | 
            -
                   | 
| 253 | 
            +
                  replica do
         | 
| 244 254 | 
             
                    value(table_read(:users).columns['flag']).must_equal({
         | 
| 245 255 | 
             
                      :type => 'tinyint(1)',
         | 
| 246 256 | 
             
                      :is_nullable => 'YES',
         | 
| @@ -256,7 +266,7 @@ describe Lhm do | |
| 256 266 | 
             
                    t.change_column(:comment, "varchar(20) DEFAULT 'none' NOT NULL")
         | 
| 257 267 | 
             
                  end
         | 
| 258 268 |  | 
| 259 | 
            -
                   | 
| 269 | 
            +
                  replica do
         | 
| 260 270 | 
             
                    value(table_read(:users).columns['comment']).must_equal({
         | 
| 261 271 | 
             
                      :type => 'varchar(20)',
         | 
| 262 272 | 
             
                      :is_nullable => 'NO',
         | 
| @@ -274,7 +284,7 @@ describe Lhm do | |
| 274 284 | 
             
                    t.change_column(:id, 'int(5)')
         | 
| 275 285 | 
             
                  end
         | 
| 276 286 |  | 
| 277 | 
            -
                   | 
| 287 | 
            +
                  replica do
         | 
| 278 288 | 
             
                    value(table_read(:small_table).columns['id']).must_equal({
         | 
| 279 289 | 
             
                      :type => 'int(5)',
         | 
| 280 290 | 
             
                      :is_nullable => 'NO',
         | 
| @@ -293,7 +303,7 @@ describe Lhm do | |
| 293 303 | 
             
                    t.rename_column(:username, :login)
         | 
| 294 304 | 
             
                  end
         | 
| 295 305 |  | 
| 296 | 
            -
                   | 
| 306 | 
            +
                  replica do
         | 
| 297 307 | 
             
                    table_data = table_read(:users)
         | 
| 298 308 | 
             
                    assert_nil table_data.columns['username']
         | 
| 299 309 | 
             
                    value(table_read(:users).columns['login']).must_equal({
         | 
| @@ -318,7 +328,7 @@ describe Lhm do | |
| 318 328 | 
             
                    t.rename_column(:group, :fnord)
         | 
| 319 329 | 
             
                  end
         | 
| 320 330 |  | 
| 321 | 
            -
                   | 
| 331 | 
            +
                  replica do
         | 
| 322 332 | 
             
                    table_data = table_read(:users)
         | 
| 323 333 | 
             
                    assert_nil table_data.columns['group']
         | 
| 324 334 | 
             
                    value(table_read(:users).columns['fnord']).must_equal({
         | 
| @@ -345,7 +355,7 @@ describe Lhm do | |
| 345 355 | 
             
                    t.rename_column(:username, :user_name)
         | 
| 346 356 | 
             
                  end
         | 
| 347 357 |  | 
| 348 | 
            -
                   | 
| 358 | 
            +
                  replica do
         | 
| 349 359 | 
             
                    table_data = table_read(:users)
         | 
| 350 360 | 
             
                    assert_nil table_data.columns['username']
         | 
| 351 361 | 
             
                    value(table_read(:users).columns['user_name']).must_equal({
         | 
| @@ -373,7 +383,7 @@ describe Lhm do | |
| 373 383 | 
             
                    t.rename_column(:reference, :ref)
         | 
| 374 384 | 
             
                  end
         | 
| 375 385 |  | 
| 376 | 
            -
                   | 
| 386 | 
            +
                  replica do
         | 
| 377 387 | 
             
                    table_data = table_read(:users)
         | 
| 378 388 | 
             
                    assert_nil table_data.columns['reference']
         | 
| 379 389 | 
             
                    value(table_read(:users).columns['ref']).must_equal({
         | 
| @@ -400,7 +410,7 @@ describe Lhm do | |
| 400 410 | 
             
                    t.rename_column(:group, :fnord)
         | 
| 401 411 | 
             
                  end
         | 
| 402 412 |  | 
| 403 | 
            -
                   | 
| 413 | 
            +
                  replica do
         | 
| 404 414 | 
             
                    table_data = table_read(:users)
         | 
| 405 415 | 
             
                    assert_nil table_data.columns['group']
         | 
| 406 416 | 
             
                    value(table_read(:users).columns['fnord']).must_equal({
         | 
| @@ -425,7 +435,7 @@ describe Lhm do | |
| 425 435 | 
             
                    t.rename_column(:username, :user_name)
         | 
| 426 436 | 
             
                  end
         | 
| 427 437 |  | 
| 428 | 
            -
                   | 
| 438 | 
            +
                  replica do
         | 
| 429 439 | 
             
                    table_data = table_read(:users)
         | 
| 430 440 | 
             
                    assert_nil table_data.columns['username']
         | 
| 431 441 | 
             
                    value(table_read(:users).columns['user_name']).must_equal({
         | 
| @@ -452,7 +462,7 @@ describe Lhm do | |
| 452 462 | 
             
                    t.rename_column(:username, :user_name)
         | 
| 453 463 | 
             
                  end
         | 
| 454 464 |  | 
| 455 | 
            -
                   | 
| 465 | 
            +
                  replica do
         | 
| 456 466 | 
             
                    table_data = table_read(:users)
         | 
| 457 467 | 
             
                    assert_nil table_data.columns['username']
         | 
| 458 468 | 
             
                    value(table_read(:users).columns['user_name']).must_equal({
         | 
| @@ -499,7 +509,7 @@ describe Lhm do | |
| 499 509 | 
             
                    end
         | 
| 500 510 | 
             
                  end
         | 
| 501 511 |  | 
| 502 | 
            -
                   | 
| 512 | 
            +
                  replica do
         | 
| 503 513 | 
             
                    table_data = table_read(:users)
         | 
| 504 514 | 
             
                    assert_nil table_data.columns['fnord']
         | 
| 505 515 | 
             
                    value(table_read(:users).columns['group']).must_equal({
         | 
| @@ -525,7 +535,7 @@ describe Lhm do | |
| 525 535 | 
             
                    t.remove_index('by')
         | 
| 526 536 | 
             
                  end
         | 
| 527 537 |  | 
| 528 | 
            -
                   | 
| 538 | 
            +
                  replica do
         | 
| 529 539 | 
             
                    value(table_read(:lines).columns).must_include 'by'
         | 
| 530 540 | 
             
                    value(table_read(:lines).columns).wont_include 'lines'
         | 
| 531 541 | 
             
                    value(index_on_columns?(:lines, ['between'], :unique)).must_equal true
         | 
| @@ -554,7 +564,7 @@ describe Lhm do | |
| 554 564 |  | 
| 555 565 | 
             
                    insert.join
         | 
| 556 566 |  | 
| 557 | 
            -
                     | 
| 567 | 
            +
                    replica do
         | 
| 558 568 | 
             
                      value(count_all(:users)).must_equal(60)
         | 
| 559 569 | 
             
                    end
         | 
| 560 570 | 
             
                  end
         | 
| @@ -577,7 +587,7 @@ describe Lhm do | |
| 577 587 |  | 
| 578 588 | 
             
                    delete.join
         | 
| 579 589 |  | 
| 580 | 
            -
                     | 
| 590 | 
            +
                    replica do
         | 
| 581 591 | 
             
                      value(count_all(:users)).must_equal(40)
         | 
| 582 592 | 
             
                    end
         | 
| 583 593 | 
             
                  end
         | 
| @@ -645,13 +655,12 @@ describe Lhm do | |
| 645 655 | 
             
                    log_lines = @logs.string.split("\n")
         | 
| 646 656 |  | 
| 647 657 | 
             
                    assert log_lines.one?{ |line| line.include?("Lost connection to MySQL, will retry to connect to same host")}
         | 
| 648 | 
            -
                    assert log_lines.any?{ |line| line.include?("Lost connection to MySQL server at 'reading initial communication packet'")}
         | 
| 649 658 | 
             
                    assert log_lines.one?{ |line| line.include?("LHM successfully reconnected to initial host")}
         | 
| 650 659 | 
             
                    assert log_lines.one?{ |line| line.include?("100% complete")}
         | 
| 651 660 |  | 
| 652 661 | 
             
                    Lhm::ChunkInsert.remove_all_callbacks
         | 
| 653 662 |  | 
| 654 | 
            -
                     | 
| 663 | 
            +
                    replica do
         | 
| 655 664 | 
             
                      value(count_all(:users)).must_equal(100)
         | 
| 656 665 | 
             
                    end
         | 
| 657 666 | 
             
                  end
         | 
| @@ -31,7 +31,7 @@ describe Lhm::LockedSwitcher do | |
| 31 31 | 
             
                  switcher = Lhm::LockedSwitcher.new(@migration, connection)
         | 
| 32 32 | 
             
                  switcher.run
         | 
| 33 33 |  | 
| 34 | 
            -
                   | 
| 34 | 
            +
                  replica do
         | 
| 35 35 | 
             
                    value(data_source_exists?(@origin)).must_equal true
         | 
| 36 36 | 
             
                    value(table_read(@migration.archive_name).columns.keys).must_include 'origin'
         | 
| 37 37 | 
             
                  end
         | 
| @@ -41,7 +41,7 @@ describe Lhm::LockedSwitcher do | |
| 41 41 | 
             
                  switcher = Lhm::LockedSwitcher.new(@migration, connection)
         | 
| 42 42 | 
             
                  switcher.run
         | 
| 43 43 |  | 
| 44 | 
            -
                   | 
| 44 | 
            +
                  replica do
         | 
| 45 45 | 
             
                    value(data_source_exists?(@destination)).must_equal false
         | 
| 46 46 | 
             
                    value(table_read(@origin.name).columns.keys).must_include 'destination'
         | 
| 47 47 | 
             
                  end
         | 
| @@ -38,7 +38,7 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do | |
| 38 38 | 
             
                  end
         | 
| 39 39 | 
             
                end
         | 
| 40 40 | 
             
                assert_equal Lhm::Error, e.class
         | 
| 41 | 
            -
                assert_match(/LHM tried the reconnection procedure but failed.  | 
| 41 | 
            +
                assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
         | 
| 42 42 | 
             
              end
         | 
| 43 43 |  | 
| 44 44 | 
             
              it "Will retry until connection is achieved" do
         | 
| @@ -61,6 +61,7 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do | |
| 61 61 | 
             
              it "Will abort if new writer is not same host" do
         | 
| 62 62 | 
             
                # The hostname will be constant before the blip
         | 
| 63 63 | 
             
                Lhm::SqlRetry.any_instance.stubs(:hostname).returns("mysql-1").then.returns("mysql-2")
         | 
| 64 | 
            +
                Lhm::SqlRetry.any_instance.stubs(:server_id).returns(1).then.returns(2)
         | 
| 64 65 |  | 
| 65 66 | 
             
                # Need new instance for stub to take into effect
         | 
| 66 67 | 
             
                lhm_retry = Lhm::SqlRetry.new(@connection, retry_options: {},
         | 
| @@ -76,12 +77,12 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do | |
| 76 77 | 
             
                end
         | 
| 77 78 |  | 
| 78 79 | 
             
                assert_equal e.class, Lhm::Error
         | 
| 79 | 
            -
                assert_match(/LHM tried the reconnection procedure but failed.  | 
| 80 | 
            +
                assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
         | 
| 80 81 |  | 
| 81 82 | 
             
                logs = @logger.string.split("\n")
         | 
| 82 83 |  | 
| 83 84 | 
             
                assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
         | 
| 84 | 
            -
                assert logs.last.include?(" | 
| 85 | 
            +
                assert logs.last.include?("Reconnected to wrong host. Started migration on: mysql-1 (server_id: 1), but reconnected to: mysql-2 (server_id: 2).")
         | 
| 85 86 | 
             
              end
         | 
| 86 87 |  | 
| 87 88 | 
             
              it "Will abort if failover happens (mimicked with proxySQL)" do
         | 
| @@ -98,11 +99,11 @@ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do | |
| 98 99 | 
             
                end
         | 
| 99 100 |  | 
| 100 101 | 
             
                assert_equal e.class, Lhm::Error
         | 
| 101 | 
            -
                assert_match(/LHM tried the reconnection procedure but failed.  | 
| 102 | 
            +
                assert_match(/LHM tried the reconnection procedure but failed. Aborting/, e.message)
         | 
| 102 103 |  | 
| 103 104 | 
             
                logs = @logger.string.split("\n")
         | 
| 104 105 |  | 
| 105 106 | 
             
                assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
         | 
| 106 | 
            -
                assert logs.last.include?(" | 
| 107 | 
            +
                assert logs.last.include?("Reconnected to wrong host. Started migration on: mysql-1 (server_id: 1), but reconnected to: mysql-2 (server_id: 2).")
         | 
| 107 108 | 
             
              end
         | 
| 108 109 | 
             
            end
         | 
| @@ -7,7 +7,7 @@ module ToxiproxyHelper | |
| 7 7 | 
             
                def included(base)
         | 
| 8 8 | 
             
                  Toxiproxy.reset
         | 
| 9 9 |  | 
| 10 | 
            -
                  # listen on localhost, but toxiproxy is in a container itself, thus the upstream uses the  | 
| 10 | 
            +
                  # listen on localhost, but toxiproxy is in a container itself, thus the upstream uses the Podman-Compose DNS
         | 
| 11 11 | 
             
                  Toxiproxy.populate(
         | 
| 12 12 | 
             
                    [
         | 
| 13 13 | 
             
                      {
         | 
    
        data/spec/test_helper.rb
    CHANGED
    
    | @@ -28,6 +28,9 @@ logger = Logger.new STDOUT | |
| 28 28 | 
             
            logger.level = Logger::WARN
         | 
| 29 29 | 
             
            Lhm.logger = logger
         | 
| 30 30 |  | 
| 31 | 
            +
            # Want test to be efficient without having to wait the normal value of 120s
         | 
| 32 | 
            +
            Lhm::SqlRetry::RECONNECT_RETRY_MAX_ITERATION = 4
         | 
| 33 | 
            +
             | 
| 31 34 | 
             
            def without_verbose(&block)
         | 
| 32 35 | 
             
              old_verbose, $VERBOSE = $VERBOSE, nil
         | 
| 33 36 | 
             
              yield
         | 
    
        data/spec/unit/printer_spec.rb
    CHANGED
    
    | @@ -70,15 +70,11 @@ describe Lhm::Printer do | |
| 70 70 | 
             
                end
         | 
| 71 71 |  | 
| 72 72 | 
             
                it 'prints the dots' do
         | 
| 73 | 
            -
                  mock  =  | 
| 74 | 
            -
                   | 
| 75 | 
            -
                    mock.expect(:write, :return_value, ['.'])
         | 
| 76 | 
            -
                  end
         | 
| 73 | 
            +
                  mock  = mock("output")
         | 
| 74 | 
            +
                  mock.expects(:write).with('.').times(10)
         | 
| 77 75 |  | 
| 78 76 | 
             
                  @printer.instance_variable_set(:@output, mock)
         | 
| 79 77 | 
             
                  10.times { @printer.notify }
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                  mock.verify
         | 
| 82 78 | 
             
                end
         | 
| 83 79 |  | 
| 84 80 | 
             
              end
         | 
| @@ -22,7 +22,7 @@ describe Lhm::SqlHelper do | |
| 22 22 | 
             
              end
         | 
| 23 23 |  | 
| 24 24 | 
             
              it 'should quote column names in index specification' do
         | 
| 25 | 
            -
                value(Lhm::SqlHelper.idx_spec(['title(10)', 'album']))
         | 
| 26 | 
            -
                  .must_equal('`title`(10), `album`')
         | 
| 25 | 
            +
                value(Lhm::SqlHelper.idx_spec(['title(10)', 'name (6)', 'album']))
         | 
| 26 | 
            +
                  .must_equal('`title`(10), `name`(6), `album`')
         | 
| 27 27 | 
             
              end
         | 
| 28 28 | 
             
            end
         |