lhm 1.2.0 → 2.2.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.
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