lhm-shopify 3.5.0 → 3.5.1

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +17 -4
  3. data/.gitignore +0 -2
  4. data/Appraisals +24 -0
  5. data/Gemfile.lock +66 -0
  6. data/README.md +42 -0
  7. data/Rakefile +1 -0
  8. data/dev.yml +18 -3
  9. data/docker-compose.yml +15 -3
  10. data/gemfiles/activerecord_5.2.gemfile +9 -0
  11. data/gemfiles/activerecord_5.2.gemfile.lock +65 -0
  12. data/gemfiles/activerecord_6.0.gemfile +7 -0
  13. data/gemfiles/activerecord_6.0.gemfile.lock +67 -0
  14. data/gemfiles/activerecord_6.1.gemfile +7 -0
  15. data/gemfiles/activerecord_6.1.gemfile.lock +66 -0
  16. data/gemfiles/activerecord_7.0.0.alpha2.gemfile +7 -0
  17. data/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +64 -0
  18. data/lhm.gemspec +7 -3
  19. data/lib/lhm/atomic_switcher.rb +1 -1
  20. data/lib/lhm/chunk_insert.rb +2 -1
  21. data/lib/lhm/chunker.rb +3 -3
  22. data/lib/lhm/cleanup/current.rb +1 -1
  23. data/lib/lhm/connection.rb +50 -11
  24. data/lib/lhm/entangler.rb +2 -2
  25. data/lib/lhm/invoker.rb +2 -2
  26. data/lib/lhm/proxysql_helper.rb +10 -0
  27. data/lib/lhm/sql_retry.rb +126 -8
  28. data/lib/lhm/throttler/slave_lag.rb +19 -2
  29. data/lib/lhm/version.rb +1 -1
  30. data/lib/lhm.rb +22 -8
  31. data/scripts/mysql/writer/create_users.sql +3 -0
  32. data/spec/integration/atomic_switcher_spec.rb +27 -10
  33. data/spec/integration/chunk_insert_spec.rb +2 -1
  34. data/spec/integration/chunker_spec.rb +1 -1
  35. data/spec/integration/database.yml +10 -0
  36. data/spec/integration/entangler_spec.rb +3 -1
  37. data/spec/integration/integration_helper.rb +23 -5
  38. data/spec/integration/lhm_spec.rb +75 -0
  39. data/spec/integration/proxysql_spec.rb +34 -0
  40. data/spec/integration/sql_retry/db_connection_helper.rb +52 -0
  41. data/spec/integration/sql_retry/lock_wait_spec.rb +8 -6
  42. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +19 -9
  43. data/spec/integration/sql_retry/proxysql_helper.rb +22 -0
  44. data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +108 -0
  45. data/spec/integration/toxiproxy_helper.rb +40 -0
  46. data/spec/test_helper.rb +21 -0
  47. data/spec/unit/chunk_insert_spec.rb +7 -2
  48. data/spec/unit/chunker_spec.rb +45 -42
  49. data/spec/unit/connection_spec.rb +22 -4
  50. data/spec/unit/entangler_spec.rb +41 -11
  51. data/spec/unit/throttler/slave_lag_spec.rb +13 -8
  52. metadata +76 -11
  53. data/gemfiles/ar-2.3_mysql.gemfile +0 -6
  54. data/gemfiles/ar-3.2_mysql.gemfile +0 -5
  55. data/gemfiles/ar-3.2_mysql2.gemfile +0 -5
  56. data/gemfiles/ar-4.0_mysql2.gemfile +0 -5
  57. data/gemfiles/ar-4.1_mysql2.gemfile +0 -5
  58. data/gemfiles/ar-4.2_mysql2.gemfile +0 -5
  59. data/gemfiles/ar-5.0_mysql2.gemfile +0 -5
@@ -2,6 +2,7 @@
2
2
  # Schmidt
3
3
 
4
4
  require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
5
+ require 'integration/toxiproxy_helper'
5
6
 
6
7
  describe Lhm do
7
8
  include IntegrationHelper
@@ -581,5 +582,79 @@ describe Lhm do
581
582
  end
582
583
  end
583
584
  end
585
+
586
+ describe 'connection' do
587
+ include ToxiproxyHelper
588
+
589
+ before(:each) do
590
+ @logs = StringIO.new
591
+ Lhm.logger = Logger.new(@logs)
592
+ end
593
+
594
+ it " should not try to reconnect if reconnect_with_consistent_host is not provided" do
595
+ connect_master_with_toxiproxy!(with_retry: false)
596
+
597
+ table_create(:users)
598
+ 100.times { |n| execute("insert into users set reference = '#{ n }'") }
599
+
600
+ assert_raises ActiveRecord::StatementInvalid do
601
+ Toxiproxy[:mysql_master].down do
602
+ Lhm.change_table(:users, :atomic_switch => false) do |t|
603
+ t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL")
604
+ t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)")
605
+ t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)")
606
+ t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT")
607
+ end
608
+ end
609
+ end
610
+ end
611
+
612
+ it "should reconnect if reconnect_with_consistent_host is true" do
613
+ connect_master_with_toxiproxy!(with_retry: true)
614
+ mysql_disabled = false
615
+
616
+ table_create(:users)
617
+ 100.times { |n| execute("insert into users set reference = '#{ n }'") }
618
+
619
+ # Redeclare Lhm::ChunkInsert to use Hook to disable MySQL writer for 3 seconds before first insert
620
+ Lhm::ChunkInsert.class_eval do
621
+ extend AfterDo
622
+
623
+ before(:insert_and_return_count_of_rows_created) do
624
+ unless mysql_disabled
625
+ mysql_disabled = true
626
+ Thread.new do
627
+ Toxiproxy[:mysql_master].down do
628
+ sleep 3
629
+ end
630
+ end
631
+ end
632
+ end
633
+
634
+ # Need to call `#method_added` manually to have the hooks take into effect
635
+ method_added(:insert_and_return_count_of_rows_created)
636
+ end
637
+
638
+ Lhm.change_table(:users, :atomic_switch => false) do |t|
639
+ t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL")
640
+ t.ddl("ALTER TABLE #{t.name} DROP PRIMARY KEY, ADD PRIMARY KEY (username, id)")
641
+ t.ddl("ALTER TABLE #{t.name} ADD INDEX (id)")
642
+ t.ddl("ALTER TABLE #{t.name} CHANGE id id bigint (20) NOT NULL AUTO_INCREMENT")
643
+ end
644
+
645
+ log_lines = @logs.string.split("\n")
646
+
647
+ 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
+ assert log_lines.one?{ |line| line.include?("LHM successfully reconnected to initial host")}
650
+ assert log_lines.one?{ |line| line.include?("100% complete")}
651
+
652
+ Lhm::ChunkInsert.remove_all_callbacks
653
+
654
+ slave do
655
+ value(count_all(:users)).must_equal(100)
656
+ end
657
+ end
658
+ end
584
659
  end
585
660
  end
@@ -0,0 +1,34 @@
1
+ describe "ProxySQL integration" do
2
+ it "Should contact the writer" do
3
+ conn = Mysql2::Client.new(
4
+ host: '127.0.0.1',
5
+ username: "writer",
6
+ password: "password",
7
+ port: "33005",
8
+ )
9
+
10
+ assert_equal conn.query("SELECT @@global.hostname as host").each.first["host"], "mysql-1"
11
+ end
12
+
13
+ it "Should contact the reader" do
14
+ conn = Mysql2::Client.new(
15
+ host: '127.0.0.1',
16
+ username: "reader",
17
+ password: "password",
18
+ port: "33005",
19
+ )
20
+
21
+ assert_equal conn.query("SELECT @@global.hostname as host").each.first["host"], "mysql-2"
22
+ end
23
+
24
+ it "Should override default hostgroup from user if rule matches" do
25
+ conn = Mysql2::Client.new(
26
+ host: '127.0.0.1',
27
+ username: "reader",
28
+ password: "password",
29
+ port: "33005",
30
+ )
31
+
32
+ assert_equal conn.query("/*maintenance:lhm*/SELECT @@global.hostname as host").each.first["host"], "mysql-1"
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+ require 'mysql2'
3
+
4
+ class DBConnectionHelper
5
+
6
+ DATABASE_CONFIG_FILE = "database.yml"
7
+
8
+ class << self
9
+ def db_config
10
+ @db_config ||= YAML.load_file(File.expand_path(File.dirname(__FILE__)) + "/../#{DATABASE_CONFIG_FILE}")
11
+ end
12
+
13
+ def new_mysql_connection(role = :master, with_data = false, toxic = false)
14
+
15
+ key = role.to_s + toxic_postfix(toxic)
16
+
17
+ conn = ActiveRecord::Base.establish_connection(
18
+ :host => '127.0.0.1',
19
+ :adapter => "mysql2",
20
+ :username => db_config[key]['user'],
21
+ :password => db_config[key]['password'],
22
+ :database => test_db_name,
23
+ :port => db_config[key]['port']
24
+ )
25
+ conn = conn.connection
26
+ init_with_dummy_data(conn) if with_data
27
+ conn
28
+ end
29
+
30
+ def toxic_postfix(toxic)
31
+ toxic ? "_toxic" : ""
32
+ end
33
+
34
+ def test_db_name
35
+ @test_db_name ||= "test"
36
+ end
37
+
38
+ def test_table_name
39
+ @test_table_name ||= "test"
40
+ end
41
+
42
+ def init_with_dummy_data(conn)
43
+ conn.execute("DROP TABLE IF EXISTS #{test_table_name} ")
44
+ conn.execute("CREATE TABLE #{test_table_name} (id int)")
45
+
46
+ 1.upto(9) do |i|
47
+ query = "INSERT INTO #{test_table_name} (id) VALUE (#{i})"
48
+ conn.execute(query)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -21,6 +21,8 @@ describe Lhm::SqlRetry do
21
21
 
22
22
  # Assert our pre-conditions
23
23
  assert_equal 2, @helper.record_count
24
+
25
+ Mysql2::Client.any_instance.stubs(:active?).returns(true)
24
26
  end
25
27
 
26
28
  after(:each) do
@@ -41,7 +43,7 @@ describe Lhm::SqlRetry do
41
43
 
42
44
  exception = assert_raises { @helper.trigger_wait_lock }
43
45
 
44
- assert_equal "Lock wait timeout exceeded; try restarting transaction", exception.message
46
+ assert_match /Lock wait timeout exceeded; try restarting transaction/, exception.message
45
47
  assert_equal Mysql2::Error::TimeoutError, exception.class
46
48
 
47
49
  assert_equal 2, @helper.record_count # no records inserted
@@ -53,12 +55,12 @@ describe Lhm::SqlRetry do
53
55
  it "successfully executes the SQL despite the errors encountered" do
54
56
  # Start a thread to retry, once the lock is held, execute the block
55
57
  @helper.with_waiting_lock do |waiting_connection|
56
- sql_retry = Lhm::SqlRetry.new(waiting_connection, {
58
+ sql_retry = Lhm::SqlRetry.new(waiting_connection, options: {
57
59
  base_interval: 0.2, # first retry after 200ms
58
60
  multiplier: 1, # subsequent retries wait 1x longer than first retry (no change)
59
61
  tries: 3, # we only need 3 tries (including the first) for the scenario described below
60
62
  rand_factor: 0 # do not introduce randomness to wait timer
61
- })
63
+ }, reconnect_with_consistent_host: false)
62
64
 
63
65
  # RetryTestHelper is configured to hold lock for 5 seconds and timeout after 2 seconds.
64
66
  # Therefore the sequence of events will be:
@@ -96,12 +98,12 @@ describe Lhm::SqlRetry do
96
98
  puts "*" * 64
97
99
  # Start a thread to retry, once the lock is held, execute the block
98
100
  @helper.with_waiting_lock do |waiting_connection|
99
- sql_retry = Lhm::SqlRetry.new(waiting_connection, {
101
+ sql_retry = Lhm::SqlRetry.new(waiting_connection, options: {
100
102
  base_interval: 0.2, # first retry after 200ms
101
103
  multiplier: 1, # subsequent retries wait 1x longer than first retry (no change)
102
104
  tries: 2, # we need 3 tries (including the first) for the scenario described below, but we only get two...we will fail
103
105
  rand_factor: 0 # do not introduce randomness to wait timer
104
- })
106
+ }, reconnect_with_consistent_host: false)
105
107
 
106
108
  # RetryTestHelper is configured to hold lock for 5 seconds and timeout after 2 seconds.
107
109
  # Therefore the sequence of events will be:
@@ -116,7 +118,7 @@ describe Lhm::SqlRetry do
116
118
 
117
119
  exception = assert_raises { @helper.trigger_wait_lock }
118
120
 
119
- assert_equal "Lock wait timeout exceeded; try restarting transaction", exception.message
121
+ assert_match /Lock wait timeout exceeded; try restarting transaction/, exception.message
120
122
  assert_equal Mysql2::Error::TimeoutError, exception.class
121
123
 
122
124
  assert_equal 2, @helper.record_count # no records inserted
@@ -1,5 +1,7 @@
1
- require 'yaml'
1
+ require 'integration/integration_helper'
2
+
2
3
  class LockWaitTimeoutTestHelper
4
+
3
5
  def initialize(lock_duration:, innodb_lock_wait_timeout:)
4
6
  # This connection will be used exclusively to setup the test,
5
7
  # assert pre-conditions and assert post-conditions.
@@ -68,7 +70,7 @@ class LockWaitTimeoutTestHelper
68
70
 
69
71
  def insert_records_at_ids(connection, ids)
70
72
  ids.each do |id|
71
- connection.query "INSERT INTO #{test_table_name} (id) VALUES (#{id})"
73
+ mysql_exec(connection, "INSERT INTO #{test_table_name} (id) VALUES (#{id})")
72
74
  end
73
75
  end
74
76
 
@@ -77,17 +79,13 @@ class LockWaitTimeoutTestHelper
77
79
  attr_reader :main_conn, :lock_duration, :innodb_lock_wait_timeout
78
80
 
79
81
  def new_mysql_connection
80
- client = Mysql2::Client.new(
82
+ Mysql2::Client.new(
81
83
  host: '127.0.0.1',
82
84
  username: db_config['master']['user'],
83
85
  password: db_config['master']['password'],
84
- port: db_config['master']['port']
86
+ port: db_config['master']['port'],
87
+ database: test_db_name
85
88
  )
86
-
87
- # For some reasons sometimes the database does not exist
88
- client.query("CREATE DATABASE IF NOT EXISTS #{test_db_name}")
89
- client.select_db(test_db_name)
90
- client
91
89
  end
92
90
 
93
91
  def test_db_name
@@ -101,4 +99,16 @@ class LockWaitTimeoutTestHelper
101
99
  def test_table_name
102
100
  @test_table_name ||= "lock_wait"
103
101
  end
102
+
103
+ private
104
+
105
+ def mysql_exec(connection, statement)
106
+ if connection.class == Mysql2::Client
107
+ connection.query(statement)
108
+ elsif connection.class.to_s.include?("ActiveRecord")
109
+ connection.execute(statement)
110
+ else
111
+ raise StandardError.new("Unrecognized MySQL client")
112
+ end
113
+ end
104
114
  end
@@ -0,0 +1,22 @@
1
+ class ProxySQLHelper
2
+ class << self
3
+ # Flips the destination hostgroup for /maintenance:lhm/ from 0 (i.e. writer) to 1 (i.e. reader)
4
+ def with_lhm_hostgroup_flip
5
+ conn = Mysql2::Client.new(
6
+ host: '127.0.0.1',
7
+ username: "remote-admin",
8
+ password: "password",
9
+ port: "6032",
10
+ )
11
+
12
+ begin
13
+ conn.query("UPDATE mysql_query_rules SET destination_hostgroup=1 WHERE match_pattern=\"maintenance:lhm\"")
14
+ conn.query("LOAD MYSQL QUERY RULES TO RUNTIME;")
15
+ yield
16
+ ensure
17
+ conn.query("UPDATE mysql_query_rules SET destination_hostgroup=0 WHERE match_pattern=\"maintenance:lhm\"")
18
+ conn.query("LOAD MYSQL QUERY RULES TO RUNTIME;")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,108 @@
1
+ require 'minitest/autorun'
2
+ require 'mysql2'
3
+ require 'lhm'
4
+ require 'toxiproxy'
5
+
6
+ require 'integration/sql_retry/lock_wait_timeout_test_helper'
7
+ require 'integration/sql_retry/db_connection_helper'
8
+ require 'integration/sql_retry/proxysql_helper'
9
+ require 'integration/toxiproxy_helper'
10
+
11
+ describe Lhm::SqlRetry, "ProxiSQL tests for LHM retry" do
12
+ include ToxiproxyHelper
13
+
14
+ before(:each) do
15
+ @old_logger = Lhm.logger
16
+ @logger = StringIO.new
17
+ Lhm.logger = Logger.new(@logger)
18
+
19
+ @connection = DBConnectionHelper::new_mysql_connection(:proxysql, true, true)
20
+
21
+ @lhm_retry = Lhm::SqlRetry.new(@connection, options: {},
22
+ reconnect_with_consistent_host: true)
23
+ end
24
+
25
+ after(:each) do
26
+ # Restore default logger
27
+ Lhm.logger = @old_logger
28
+ end
29
+
30
+ it "Will abort if service is down" do
31
+
32
+ e = assert_raises Lhm::Error do
33
+ #Service down
34
+ Toxiproxy[:mysql_proxysql].down do
35
+ @lhm_retry.with_retries do |retriable_connection|
36
+ retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
37
+ end
38
+ end
39
+ end
40
+ assert_equal Lhm::Error, e.class
41
+ assert_match(/LHM tried the reconnection procedure but failed. Latest error:/, e.message)
42
+ end
43
+
44
+ it "Will retry until connection is achieved" do
45
+
46
+ #Creating a network blip
47
+ ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
48
+ @lhm_retry.with_retries do |retriable_connection|
49
+ retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
50
+ end
51
+ end
52
+
53
+ assert_equal @connection.execute("Select * from #{DBConnectionHelper.test_table_name} WHERE id=2000").to_a.first.first, 2000
54
+
55
+ logs = @logger.string.split("\n")
56
+
57
+ assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
58
+ assert logs.last.include?("LHM successfully reconnected to initial host")
59
+ end
60
+
61
+ it "Will abort if new writer is not same host" do
62
+ # The hostname will be constant before the blip
63
+ Lhm::SqlRetry.any_instance.stubs(:hostname).returns("mysql-1").then.returns("mysql-2")
64
+
65
+ # Need new instance for stub to take into effect
66
+ lhm_retry = Lhm::SqlRetry.new(@connection, options: {},
67
+ reconnect_with_consistent_host: true)
68
+
69
+ e = assert_raises Lhm::Error do
70
+ #Creating a network blip
71
+ ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
72
+ lhm_retry.with_retries do |retriable_connection|
73
+ retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
74
+ end
75
+ end
76
+ end
77
+
78
+ assert_equal e.class, Lhm::Error
79
+ assert_match(/LHM tried the reconnection procedure but failed. Latest error: Reconnected to wrong host/, e.message)
80
+
81
+ logs = @logger.string.split("\n")
82
+
83
+ assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
84
+ assert logs.last.include?("Lost connection to MySQL server at 'reading initial communication packet")
85
+ end
86
+
87
+ it "Will abort if failover happens (mimicked with proxySQL)" do
88
+ e = assert_raises Lhm::Error do
89
+ #Creates a failover by switching the target hostgroup for the #hostname
90
+ ProxySQLHelper.with_lhm_hostgroup_flip do
91
+ #Creating a network blip
92
+ ToxiproxyHelper.with_kill_and_restart(:mysql_proxysql, 2.seconds) do
93
+ @lhm_retry.with_retries do |retriable_connection|
94
+ retriable_connection.execute("INSERT INTO #{DBConnectionHelper.test_table_name} (id) VALUES (2000)")
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ assert_equal e.class, Lhm::Error
101
+ assert_match(/LHM tried the reconnection procedure but failed. Latest error: Reconnected to wrong host/, e.message)
102
+
103
+ logs = @logger.string.split("\n")
104
+
105
+ assert logs.first.include?("Lost connection to MySQL, will retry to connect to same host")
106
+ assert logs.last.include?("Lost connection to MySQL server at 'reading initial communication packet")
107
+ end
108
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require 'toxiproxy'
3
+
4
+ module ToxiproxyHelper
5
+ class << self
6
+
7
+ def included(base)
8
+ Toxiproxy.reset
9
+
10
+ # listen on localhost, but toxiproxy is in a container itself, thus the upstream uses the Docker-Compose DNS
11
+ Toxiproxy.populate(
12
+ [
13
+ {
14
+ name: 'mysql_master',
15
+ listen: '0.0.0.0:22220',
16
+ upstream: 'mysql-1:3306'
17
+ },
18
+ {
19
+ name: 'mysql_proxysql',
20
+ listen: '0.0.0.0:22222',
21
+ upstream: 'proxysql:3306'
22
+ }
23
+ ])
24
+ end
25
+
26
+ def with_kill_and_restart(target, restart_after)
27
+ thread = Thread.new do
28
+ sleep(restart_after) unless restart_after.nil?
29
+ Toxiproxy[target].enable
30
+ end
31
+
32
+ Toxiproxy[target].disable
33
+
34
+ yield
35
+
36
+ ensure
37
+ thread.join
38
+ end
39
+ end
40
+ end
data/spec/test_helper.rb CHANGED
@@ -10,6 +10,8 @@ require 'minitest/autorun'
10
10
  require 'minitest/spec'
11
11
  require 'minitest/mock'
12
12
  require 'mocha/minitest'
13
+ require 'after_do'
14
+ require 'byebug'
13
15
  require 'pathname'
14
16
  require 'lhm'
15
17
 
@@ -17,6 +19,8 @@ $project = Pathname.new(File.dirname(__FILE__) + '/..').cleanpath
17
19
  $spec = $project.join('spec')
18
20
  $fixtures = $spec.join('fixtures')
19
21
 
22
+ $db_name = 'test'
23
+
20
24
  require 'active_record'
21
25
  require 'mysql2'
22
26
 
@@ -43,3 +47,20 @@ end
43
47
  def throttler
44
48
  Lhm::Throttler::Time.new(:stride => 100)
45
49
  end
50
+
51
+ def init_test_db
52
+ db_config = YAML.load_file(File.expand_path(File.dirname(__FILE__)) + '/integration/database.yml')
53
+ conn = Mysql2::Client.new(
54
+ :host => '127.0.0.1',
55
+ :username => db_config['master']['user'],
56
+ :password => db_config['master']['password'],
57
+ :port => db_config['master']['port']
58
+ )
59
+
60
+ conn.query("DROP DATABASE IF EXISTS #{$db_name}")
61
+ conn.query("CREATE DATABASE #{$db_name}")
62
+ end
63
+
64
+ init_test_db
65
+
66
+
@@ -4,17 +4,22 @@
4
4
  require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
5
 
6
6
  require 'lhm/chunk_insert'
7
+ require 'lhm/connection'
7
8
 
8
9
  describe Lhm::ChunkInsert do
9
10
  before(:each) do
10
- @connection = stub(:connection)
11
+ ar_connection = mock()
12
+ ar_connection.stubs(:execute).returns([["dummy"]])
13
+ @connection = Lhm::Connection.new(connection: ar_connection, options: {reconnect_with_consistent_host: false})
11
14
  @origin = Lhm::Table.new('foo')
12
15
  @destination = Lhm::Table.new('bar')
13
16
  end
14
17
 
15
18
  describe "#sql" do
16
19
  describe "when migration has no conditions" do
17
- before { @migration = Lhm::Migration.new(@origin, @destination) }
20
+ before do
21
+ @migration = Lhm::Migration.new(@origin, @destination)
22
+ end
18
23
 
19
24
  it "uses a simple where clause" do
20
25
  assert_equal(