lhm 1.2.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +256 -0
  4. data/.travis.yml +5 -1
  5. data/CHANGELOG.md +26 -0
  6. data/README.md +87 -8
  7. data/Rakefile +6 -4
  8. data/bin/lhm-config.sh +7 -0
  9. data/bin/lhm-kill-queue +13 -15
  10. data/bin/lhm-spec-clobber.sh +5 -4
  11. data/bin/lhm-spec-grants.sh +2 -2
  12. data/bin/lhm-spec-setup-cluster.sh +2 -3
  13. data/gemfiles/ar-2.3_mysql.gemfile +2 -1
  14. data/gemfiles/ar-3.2_mysql.gemfile +1 -1
  15. data/gemfiles/ar-3.2_mysql2.gemfile +1 -1
  16. data/gemfiles/dm_mysql.gemfile +1 -1
  17. data/lhm.gemspec +7 -8
  18. data/lib/lhm/atomic_switcher.rb +2 -1
  19. data/lib/lhm/chunker.rb +51 -39
  20. data/lib/lhm/command.rb +4 -2
  21. data/lib/lhm/connection.rb +14 -2
  22. data/lib/lhm/entangler.rb +5 -5
  23. data/lib/lhm/intersection.rb +29 -16
  24. data/lib/lhm/invoker.rb +31 -10
  25. data/lib/lhm/locked_switcher.rb +6 -6
  26. data/lib/lhm/migration.rb +7 -5
  27. data/lib/lhm/migrator.rb +57 -9
  28. data/lib/lhm/printer.rb +54 -0
  29. data/lib/lhm/sql_helper.rb +4 -4
  30. data/lib/lhm/table.rb +12 -12
  31. data/lib/lhm/throttler/time.rb +29 -0
  32. data/lib/lhm/throttler.rb +32 -0
  33. data/lib/lhm/version.rb +1 -1
  34. data/lib/lhm.rb +71 -6
  35. data/spec/.lhm.example +1 -1
  36. data/spec/README.md +20 -13
  37. data/spec/fixtures/lines.ddl +7 -0
  38. data/spec/fixtures/permissions.ddl +5 -0
  39. data/spec/fixtures/tracks.ddl +5 -0
  40. data/spec/fixtures/users.ddl +4 -2
  41. data/spec/integration/atomic_switcher_spec.rb +7 -7
  42. data/spec/integration/chunker_spec.rb +11 -5
  43. data/spec/integration/cleanup_spec.rb +72 -0
  44. data/spec/integration/entangler_spec.rb +11 -11
  45. data/spec/integration/integration_helper.rb +49 -17
  46. data/spec/integration/lhm_spec.rb +157 -37
  47. data/spec/integration/locked_switcher_spec.rb +7 -7
  48. data/spec/integration/table_spec.rb +15 -17
  49. data/spec/test_helper.rb +28 -0
  50. data/spec/unit/atomic_switcher_spec.rb +6 -6
  51. data/spec/unit/chunker_spec.rb +95 -73
  52. data/spec/unit/datamapper_connection_spec.rb +1 -0
  53. data/spec/unit/entangler_spec.rb +19 -19
  54. data/spec/unit/intersection_spec.rb +27 -15
  55. data/spec/unit/lhm_spec.rb +29 -0
  56. data/spec/unit/locked_switcher_spec.rb +14 -14
  57. data/spec/unit/migration_spec.rb +10 -5
  58. data/spec/unit/migrator_spec.rb +53 -41
  59. data/spec/unit/printer_spec.rb +79 -0
  60. data/spec/unit/sql_helper_spec.rb +10 -10
  61. data/spec/unit/table_spec.rb +11 -11
  62. data/spec/unit/throttler_spec.rb +73 -0
  63. data/spec/unit/unit_helper.rb +1 -13
  64. metadata +63 -24
  65. data/spec/bootstrap.rb +0 -13
data/lib/lhm/table.rb CHANGED
@@ -7,7 +7,7 @@ module Lhm
7
7
  class Table
8
8
  attr_reader :name, :columns, :indices, :pk, :ddl
9
9
 
10
- def initialize(name, pk = "id", ddl = nil)
10
+ def initialize(name, pk = 'id', ddl = nil)
11
11
  @name = name
12
12
  @columns = {}
13
13
  @indices = {}
@@ -16,7 +16,7 @@ module Lhm
16
16
  end
17
17
 
18
18
  def satisfies_primary_key?
19
- @pk == "id"
19
+ @pk == 'id'
20
20
  end
21
21
 
22
22
  def destination_name
@@ -45,10 +45,10 @@ module Lhm
45
45
 
46
46
  Table.new(@table_name, extract_primary_key(schema), ddl).tap do |table|
47
47
  schema.each do |defn|
48
- column_name = struct_key(defn, "COLUMN_NAME")
49
- column_type = struct_key(defn, "COLUMN_TYPE")
50
- is_nullable = struct_key(defn, "IS_NULLABLE")
51
- column_default = struct_key(defn, "COLUMN_DEFAULT")
48
+ column_name = struct_key(defn, 'COLUMN_NAME')
49
+ column_type = struct_key(defn, 'COLUMN_TYPE')
50
+ is_nullable = struct_key(defn, 'IS_NULLABLE')
51
+ column_default = struct_key(defn, 'COLUMN_DEFAULT')
52
52
  table.columns[defn[column_name]] = {
53
53
  :type => defn[column_type],
54
54
  :is_nullable => defn[is_nullable],
@@ -83,11 +83,11 @@ module Lhm
83
83
  def extract_indices(indices)
84
84
  indices.
85
85
  map do |row|
86
- key_name = struct_key(row, "Key_name")
87
- column_name = struct_key(row, "COLUMN_NAME")
86
+ key_name = struct_key(row, 'Key_name')
87
+ column_name = struct_key(row, 'COLUMN_NAME')
88
88
  [row[key_name], row[column_name]]
89
89
  end.
90
- inject(Hash.new { |h, k| h[k] = []}) do |memo, (idx, column)|
90
+ inject(Hash.new { |h, k| h[k] = [] }) do |memo, (idx, column)|
91
91
  memo[idx] << column
92
92
  memo
93
93
  end
@@ -95,12 +95,12 @@ module Lhm
95
95
 
96
96
  def extract_primary_key(schema)
97
97
  cols = schema.select do |defn|
98
- column_key = struct_key(defn, "COLUMN_KEY")
99
- defn[column_key] == "PRI"
98
+ column_key = struct_key(defn, 'COLUMN_KEY')
99
+ defn[column_key] == 'PRI'
100
100
  end
101
101
 
102
102
  keys = cols.map do |defn|
103
- column_name = struct_key(defn, "COLUMN_NAME")
103
+ column_name = struct_key(defn, 'COLUMN_NAME')
104
104
  defn[column_name]
105
105
  end
106
106
 
@@ -0,0 +1,29 @@
1
+ module Lhm
2
+ module Throttler
3
+ class Time
4
+ include Command
5
+
6
+ DEFAULT_TIMEOUT = 0.1
7
+ DEFAULT_STRIDE = 40_000
8
+
9
+ attr_accessor :timeout_seconds
10
+ attr_accessor :stride
11
+
12
+ def initialize(options = {})
13
+ @timeout_seconds = options[:delay] || DEFAULT_TIMEOUT
14
+ @stride = options[:stride] || DEFAULT_STRIDE
15
+ end
16
+
17
+ def execute
18
+ sleep timeout_seconds
19
+ end
20
+ end
21
+
22
+ class LegacyTime < Time
23
+ def initialize(timeout, stride)
24
+ @timeout_seconds = timeout / 1000.0
25
+ @stride = stride
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ require 'lhm/throttler/time'
2
+
3
+ module Lhm
4
+ module Throttler
5
+ CLASSES = { :time_throttler => Throttler::Time }
6
+
7
+ def throttler
8
+ @throttler ||= Throttler::Time.new
9
+ end
10
+
11
+ def setup_throttler(type, options = {})
12
+ @throttler = Factory.create_throttler(type, options)
13
+ end
14
+
15
+ class Factory
16
+ def self.create_throttler(type, options = {})
17
+ case type
18
+ when Lhm::Command
19
+ type
20
+ when Symbol
21
+ CLASSES[type].new(options)
22
+ when String
23
+ CLASSES[type.to_sym].new(options)
24
+ when Class
25
+ type.new(options)
26
+ else
27
+ raise ArgumentError, 'type argument must be a Symbol, String or Class'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/lhm/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Schmidt
3
3
 
4
4
  module Lhm
5
- VERSION = "1.2.0"
5
+ VERSION = '2.2.0'
6
6
  end
data/lib/lhm.rb CHANGED
@@ -4,7 +4,9 @@
4
4
  require 'lhm/table'
5
5
  require 'lhm/invoker'
6
6
  require 'lhm/connection'
7
+ require 'lhm/throttler'
7
8
  require 'lhm/version'
9
+ require 'logger'
8
10
 
9
11
  # Large hadron migrator - online schema change tool
10
12
  #
@@ -17,6 +19,10 @@ require 'lhm/version'
17
19
  # end
18
20
  #
19
21
  module Lhm
22
+ extend Throttler
23
+ extend self
24
+
25
+ DEFAULT_LOGGER_OPTIONS = { level: Logger::INFO, file: STDOUT }
20
26
 
21
27
  # Alters a table with the changes described in the block
22
28
  #
@@ -26,6 +32,10 @@ module Lhm
26
32
  # Size of a chunk (defaults to: 40,000)
27
33
  # @option options [Fixnum] :throttle
28
34
  # Time to wait between chunks in milliseconds (defaults to: 100)
35
+ # @option options [Fixnum] :start
36
+ # Primary Key position at which to start copying chunks
37
+ # @option options [Fixnum] :limit
38
+ # Primary Key position at which to stop copying chunks
29
39
  # @option options [Boolean] :atomic_switch
30
40
  # Use atomic switch to rename tables (defaults to: true)
31
41
  # If using a version of mysql affected by atomic switch bug, LHM forces user
@@ -33,26 +43,81 @@ module Lhm
33
43
  # @yield [Migrator] Yielded Migrator object records the changes
34
44
  # @return [Boolean] Returns true if the migration finishes
35
45
  # @raise [Error] Raises Lhm::Error in case of a error and aborts the migration
36
- def self.change_table(table_name, options = {}, &block)
37
- connection = Connection.new(adapter)
38
-
46
+ def change_table(table_name, options = {}, &block)
39
47
  origin = Table.parse(table_name, connection)
40
48
  invoker = Invoker.new(origin, connection)
41
49
  block.call(invoker.migrator)
42
50
  invoker.run(options)
43
-
44
51
  true
45
52
  end
46
53
 
47
- def self.setup(adapter)
54
+ # Cleanup tables and triggers
55
+ #
56
+ # @param [Boolean] run execute now or just display information
57
+ # @param [Hash] options Optional options to alter cleanup behaviour
58
+ # @option options [Time] :until
59
+ # Filter to only remove tables up to specified time (defaults to: nil)
60
+ def cleanup(run = false, options = {})
61
+ lhm_tables = connection.select_values('show tables').select { |name| name =~ /^lhm(a|n)_/ }
62
+ if options[:until]
63
+ lhm_tables.select! { |table|
64
+ table_date_time = Time.strptime(table, 'lhma_%Y_%m_%d_%H_%M_%S')
65
+ table_date_time <= options[:until]
66
+ }
67
+ end
68
+
69
+ lhm_triggers = connection.select_values('show triggers').collect do |trigger|
70
+ trigger.respond_to?(:trigger) ? trigger.trigger : trigger
71
+ end.select { |name| name =~ /^lhmt/ }
72
+
73
+ if run
74
+ lhm_triggers.each do |trigger|
75
+ connection.execute("drop trigger if exists #{trigger}")
76
+ end
77
+ lhm_tables.each do |table|
78
+ connection.execute("drop table if exists #{table}")
79
+ end
80
+ true
81
+ elsif lhm_tables.empty? && lhm_triggers.empty?
82
+ puts 'Everything is clean. Nothing to do.'
83
+ true
84
+ else
85
+ puts "Existing LHM backup tables: #{lhm_tables.join(', ')}."
86
+ puts "Existing LHM triggers: #{lhm_triggers.join(', ')}."
87
+ puts 'Run Lhm.cleanup(true) to drop them all.'
88
+ false
89
+ end
90
+ end
91
+
92
+ def setup(adapter)
48
93
  @@adapter = adapter
49
94
  end
50
95
 
51
- def self.adapter
96
+ def adapter
52
97
  @@adapter ||=
53
98
  begin
54
99
  raise 'Please call Lhm.setup' unless defined?(ActiveRecord)
55
100
  ActiveRecord::Base.connection
56
101
  end
57
102
  end
103
+
104
+ def self.logger=(new_logger)
105
+ @@logger = new_logger
106
+ end
107
+
108
+ def self.logger
109
+ @@logger ||=
110
+ begin
111
+ logger = Logger.new(DEFAULT_LOGGER_OPTIONS[:file])
112
+ logger.level = DEFAULT_LOGGER_OPTIONS[:level]
113
+ logger.formatter = nil
114
+ logger
115
+ end
116
+ end
117
+
118
+ protected
119
+
120
+ def connection
121
+ Connection.new(adapter)
122
+ end
58
123
  end
data/spec/.lhm.example CHANGED
@@ -1,4 +1,4 @@
1
1
  mysqldir=/usr/local/mysql
2
- basedir=/opt/lhm-cluster
2
+ basedir=~/lhm-cluster
3
3
  master_port=3306
4
4
  slave_port=3307
data/spec/README.md CHANGED
@@ -1,47 +1,54 @@
1
- Preparing for master slave integration tests
2
- --------------------------------------------
1
+ # Preparing for master slave integration tests
3
2
 
4
- # configuration
3
+ ## Configuration
5
4
 
6
5
  create ~/.lhm:
7
6
 
8
7
  mysqldir=/usr/local/mysql
9
- basedir=/opt/lhm-cluster
8
+ basedir=~/lhm-cluster
10
9
  master_port=3306
11
10
  slave_port=3307
12
11
 
13
12
  mysqldir specifies the location of your mysql install. basedir is the
14
13
  directory master and slave databases will get installed into.
15
14
 
16
- # setup
15
+ ## Automatic setup
16
+
17
+ ### Run
18
+
19
+ bin/lhm-spec-clobber.sh
17
20
 
18
21
  You can set the integration specs up to run against a master slave setup by
19
- running the included `bin/lhm-spec-clobber.sh` script. this deletes the configured
20
- lhm master slave setup and reinstalls and configures a master slave setup.
22
+ running the included that. This deletes the configured lhm master slave setup and reinstalls and configures a master slave setup.
21
23
 
22
24
  Follow the manual instructions if you want more control over this process.
23
25
 
24
- # manual setup
26
+ ## Manual setup
25
27
 
26
- ## set up instances
28
+ ### set up instances
27
29
 
28
30
  bin/lhm-spec-setup-cluster.sh
29
31
 
30
- ## start instances
32
+ ### start instances
31
33
 
32
34
  basedir=/opt/lhm-luster
33
35
  mysqld --defaults-file="$basedir/master/my.cnf"
34
36
  mysqld --defaults-file="$basedir/slave/my.cnf"
35
37
 
36
- ## run the grants
38
+ ### run the grants
37
39
 
38
40
  bin/lhm-spec-grants.sh
39
41
 
40
42
  ## run specs
41
43
 
42
- To run specs in slave mode, set the SLAVE=1 when running tests:
44
+ Setup the dependency gems
45
+
46
+ export BUNDLE_GEMFILE=gemfiles/ar-3.2_mysql2.gemfile
47
+ bundle install
48
+
49
+ To run specs in slave mode, set the MASTER_SLAVE=1 when running tests:
43
50
 
44
- MASTER_SLAVE=1 rake specs
51
+ MASTER_SLAVE=1 bundle exec rake specs
45
52
 
46
53
  # connecting
47
54
 
@@ -0,0 +1,7 @@
1
+ CREATE TABLE `lines` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `between` varchar(10),
4
+ `lines` int(11),
5
+ `key` varchar(10),
6
+ PRIMARY KEY (`id`)
7
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,5 @@
1
+ CREATE TABLE `permissions` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `track_id` int(11) DEFAULT NULL,
4
+ PRIMARY KEY (`id`)
5
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -0,0 +1,5 @@
1
+ CREATE TABLE `tracks` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `public` int(4) DEFAULT 0,
4
+ PRIMARY KEY (`id`)
5
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -2,11 +2,13 @@ CREATE TABLE `users` (
2
2
  `id` int(11) NOT NULL AUTO_INCREMENT,
3
3
  `reference` int(11) DEFAULT NULL,
4
4
  `username` varchar(255) DEFAULT NULL,
5
- `group` varchar(255) DEFAULT NULL,
5
+ `group` varchar(255) DEFAULT 'Superfriends',
6
6
  `created_at` datetime DEFAULT NULL,
7
7
  `comment` varchar(20) DEFAULT NULL,
8
8
  `description` text,
9
9
  PRIMARY KEY (`id`),
10
10
  UNIQUE KEY `index_users_on_reference` (`reference`),
11
- KEY `index_users_on_username_and_created_at` (`username`,`created_at`)
11
+ KEY `index_users_on_username_and_created_at` (`username`,`created_at`),
12
+ KEY `index_with_a_custom_name` (`username`,`group`)
13
+
12
14
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8
@@ -12,30 +12,30 @@ describe Lhm::AtomicSwitcher do
12
12
 
13
13
  before(:each) { connect_master! }
14
14
 
15
- describe "switching" do
15
+ describe 'switching' do
16
16
  before(:each) do
17
- @origin = table_create("origin")
18
- @destination = table_create("destination")
17
+ @origin = table_create('origin')
18
+ @destination = table_create('destination')
19
19
  @migration = Lhm::Migration.new(@origin, @destination)
20
20
  end
21
21
 
22
- it "rename origin to archive" do
22
+ it 'rename origin to archive' do
23
23
  switcher = Lhm::AtomicSwitcher.new(@migration, connection)
24
24
  switcher.run
25
25
 
26
26
  slave do
27
27
  table_exists?(@origin).must_equal true
28
- table_read(@migration.archive_name).columns.keys.must_include "origin"
28
+ table_read(@migration.archive_name).columns.keys.must_include 'origin'
29
29
  end
30
30
  end
31
31
 
32
- it "rename destination to origin" do
32
+ it 'rename destination to origin' do
33
33
  switcher = Lhm::AtomicSwitcher.new(@migration, connection)
34
34
  switcher.run
35
35
 
36
36
  slave do
37
37
  table_exists?(@destination).must_equal false
38
- table_read(@origin.name).columns.keys.must_include "destination"
38
+ table_read(@origin.name).columns.keys.must_include 'destination'
39
39
  end
40
40
  end
41
41
  end
@@ -2,8 +2,6 @@
2
2
  # Schmidt
3
3
 
4
4
  require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
5
-
6
- require 'lhm'
7
5
  require 'lhm/table'
8
6
  require 'lhm/migration'
9
7
 
@@ -12,21 +10,29 @@ describe Lhm::Chunker do
12
10
 
13
11
  before(:each) { connect_master! }
14
12
 
15
- describe "copying" do
13
+ describe 'copying' do
16
14
  before(:each) do
17
15
  @origin = table_create(:origin)
18
16
  @destination = table_create(:destination)
19
17
  @migration = Lhm::Migration.new(@origin, @destination)
20
18
  end
21
19
 
22
- it "should copy 23 rows from origin to destination" do
20
+ it 'should copy 23 rows from origin to destination' do
23
21
  23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
24
22
 
25
- Lhm::Chunker.new(@migration, connection, { :stride => 100 }).run
23
+ printer = MiniTest::Mock.new
24
+ 5.times { printer.expect(:notify, :return_value, [Fixnum, Fixnum]) }
25
+ printer.expect(:end, :return_value, [])
26
+
27
+ Lhm::Chunker.new(
28
+ @migration, connection, { :throttler => Lhm::Throttler::Time.new(:stride => 100), :printer => printer }
29
+ ).run
26
30
 
27
31
  slave do
28
32
  count_all(@destination.name).must_equal(23)
29
33
  end
34
+
35
+ printer.verify
30
36
  end
31
37
  end
32
38
  end
@@ -0,0 +1,72 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
5
+
6
+ describe Lhm, 'cleanup' do
7
+ include IntegrationHelper
8
+ before(:each) { connect_master! }
9
+
10
+ describe 'changes' do
11
+ before(:each) do
12
+ table_create(:users)
13
+ simulate_failed_migration do
14
+ Lhm.change_table(:users, :atomic_switch => false) do |t|
15
+ t.add_column(:logins, "INT(12) DEFAULT '0'")
16
+ t.add_index(:logins)
17
+ end
18
+ end
19
+ end
20
+
21
+ after(:each) do
22
+ Lhm.cleanup(true)
23
+ end
24
+
25
+ it 'should show temporary tables' do
26
+ output = capture_stdout do
27
+ Lhm.cleanup
28
+ end
29
+ output.must_include('Existing LHM backup tables')
30
+ output.must_match(/lhma_[0-9_]*_users/)
31
+ end
32
+
33
+ it 'should show temporary tables within range' do
34
+ table = OpenStruct.new(:name => 'users')
35
+ table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now - 172800).archive_name
36
+ table_rename(:users, table_name)
37
+
38
+ output = capture_stdout do
39
+ Lhm.cleanup false, { :until => Time.now - 86400 }
40
+ end
41
+ output.must_include('Existing LHM backup tables')
42
+ output.must_match(/lhma_[0-9_]*_users/)
43
+ end
44
+
45
+ it 'should exclude temporary tables outside range' do
46
+ table = OpenStruct.new(:name => 'users')
47
+ table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now).archive_name
48
+ table_rename(:users, table_name)
49
+
50
+ output = capture_stdout do
51
+ Lhm.cleanup false, { :until => Time.now - 172800 }
52
+ end
53
+ output.must_include('Existing LHM backup tables')
54
+ output.wont_match(/lhma_[0-9_]*_users/)
55
+ end
56
+
57
+ it 'should show temporary triggers' do
58
+ output = capture_stdout do
59
+ Lhm.cleanup
60
+ end
61
+ output.must_include('Existing LHM triggers')
62
+ output.must_include('lhmt_ins_users')
63
+ output.must_include('lhmt_del')
64
+ output.must_include('lhmt_upd_users')
65
+ end
66
+
67
+ it 'should delete temporary tables' do
68
+ Lhm.cleanup(true).must_equal(true)
69
+ Lhm.cleanup.must_equal(true)
70
+ end
71
+ end
72
+ end
@@ -12,25 +12,25 @@ describe Lhm::Entangler do
12
12
 
13
13
  before(:each) { connect_master! }
14
14
 
15
- describe "entanglement" do
15
+ describe 'entanglement' do
16
16
  before(:each) do
17
- @origin = table_create("origin")
18
- @destination = table_create("destination")
17
+ @origin = table_create('origin')
18
+ @destination = table_create('destination')
19
19
  @migration = Lhm::Migration.new(@origin, @destination)
20
20
  @entangler = Lhm::Entangler.new(@migration, connection)
21
21
  end
22
22
 
23
- it "should replay inserts from origin into destination" do
23
+ it 'should replay inserts from origin into destination' do
24
24
  @entangler.run do |entangler|
25
25
  execute("insert into origin (common) values ('inserted')")
26
26
  end
27
27
 
28
28
  slave do
29
- count(:destination, "common", "inserted").must_equal(1)
29
+ count(:destination, 'common', 'inserted').must_equal(1)
30
30
  end
31
31
  end
32
32
 
33
- it "should replay deletes from origin into destination" do
33
+ it 'should replay deletes from origin into destination' do
34
34
  execute("insert into origin (common) values ('inserted')")
35
35
 
36
36
  @entangler.run do |entangler|
@@ -38,28 +38,28 @@ describe Lhm::Entangler do
38
38
  end
39
39
 
40
40
  slave do
41
- count(:destination, "common", "inserted").must_equal(0)
41
+ count(:destination, 'common', 'inserted').must_equal(0)
42
42
  end
43
43
  end
44
44
 
45
- it "should replay updates from origin into destination" do
45
+ it 'should replay updates from origin into destination' do
46
46
  @entangler.run do |entangler|
47
47
  execute("insert into origin (common) values ('inserted')")
48
48
  execute("update origin set common = 'updated'")
49
49
  end
50
50
 
51
51
  slave do
52
- count(:destination, "common", "updated").must_equal(1)
52
+ count(:destination, 'common', 'updated').must_equal(1)
53
53
  end
54
54
  end
55
55
 
56
- it "should remove entanglement" do
56
+ it 'should remove entanglement' do
57
57
  @entangler.run {}
58
58
 
59
59
  execute("insert into origin (common) values ('inserted')")
60
60
 
61
61
  slave do
62
- count(:destination, "common", "inserted").must_equal(0)
62
+ count(:destination, 'common', 'inserted').must_equal(0)
63
63
  end
64
64
  end
65
65
  end