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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +256 -0
- data/.travis.yml +5 -1
- data/CHANGELOG.md +26 -0
- data/README.md +87 -8
- data/Rakefile +6 -4
- data/bin/lhm-config.sh +7 -0
- data/bin/lhm-kill-queue +13 -15
- data/bin/lhm-spec-clobber.sh +5 -4
- data/bin/lhm-spec-grants.sh +2 -2
- data/bin/lhm-spec-setup-cluster.sh +2 -3
- data/gemfiles/ar-2.3_mysql.gemfile +2 -1
- data/gemfiles/ar-3.2_mysql.gemfile +1 -1
- data/gemfiles/ar-3.2_mysql2.gemfile +1 -1
- data/gemfiles/dm_mysql.gemfile +1 -1
- data/lhm.gemspec +7 -8
- data/lib/lhm/atomic_switcher.rb +2 -1
- data/lib/lhm/chunker.rb +51 -39
- data/lib/lhm/command.rb +4 -2
- data/lib/lhm/connection.rb +14 -2
- data/lib/lhm/entangler.rb +5 -5
- data/lib/lhm/intersection.rb +29 -16
- data/lib/lhm/invoker.rb +31 -10
- data/lib/lhm/locked_switcher.rb +6 -6
- data/lib/lhm/migration.rb +7 -5
- data/lib/lhm/migrator.rb +57 -9
- data/lib/lhm/printer.rb +54 -0
- data/lib/lhm/sql_helper.rb +4 -4
- data/lib/lhm/table.rb +12 -12
- data/lib/lhm/throttler/time.rb +29 -0
- data/lib/lhm/throttler.rb +32 -0
- data/lib/lhm/version.rb +1 -1
- data/lib/lhm.rb +71 -6
- data/spec/.lhm.example +1 -1
- data/spec/README.md +20 -13
- data/spec/fixtures/lines.ddl +7 -0
- data/spec/fixtures/permissions.ddl +5 -0
- data/spec/fixtures/tracks.ddl +5 -0
- data/spec/fixtures/users.ddl +4 -2
- data/spec/integration/atomic_switcher_spec.rb +7 -7
- data/spec/integration/chunker_spec.rb +11 -5
- data/spec/integration/cleanup_spec.rb +72 -0
- data/spec/integration/entangler_spec.rb +11 -11
- data/spec/integration/integration_helper.rb +49 -17
- data/spec/integration/lhm_spec.rb +157 -37
- data/spec/integration/locked_switcher_spec.rb +7 -7
- data/spec/integration/table_spec.rb +15 -17
- data/spec/test_helper.rb +28 -0
- data/spec/unit/atomic_switcher_spec.rb +6 -6
- data/spec/unit/chunker_spec.rb +95 -73
- data/spec/unit/datamapper_connection_spec.rb +1 -0
- data/spec/unit/entangler_spec.rb +19 -19
- data/spec/unit/intersection_spec.rb +27 -15
- data/spec/unit/lhm_spec.rb +29 -0
- data/spec/unit/locked_switcher_spec.rb +14 -14
- data/spec/unit/migration_spec.rb +10 -5
- data/spec/unit/migrator_spec.rb +53 -41
- data/spec/unit/printer_spec.rb +79 -0
- data/spec/unit/sql_helper_spec.rb +10 -10
- data/spec/unit/table_spec.rb +11 -11
- data/spec/unit/throttler_spec.rb +73 -0
- data/spec/unit/unit_helper.rb +1 -13
- metadata +63 -24
- 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 =
|
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 ==
|
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,
|
49
|
-
column_type = struct_key(defn,
|
50
|
-
is_nullable = struct_key(defn,
|
51
|
-
column_default = struct_key(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')
|
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,
|
87
|
-
column_name = struct_key(row,
|
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,
|
99
|
-
defn[column_key] ==
|
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,
|
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
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
|
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
|
-
|
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
|
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
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
|
-
|
3
|
+
## Configuration
|
5
4
|
|
6
5
|
create ~/.lhm:
|
7
6
|
|
8
7
|
mysqldir=/usr/local/mysql
|
9
|
-
basedir
|
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
|
-
|
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
|
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
|
-
|
26
|
+
## Manual setup
|
25
27
|
|
26
|
-
|
28
|
+
### set up instances
|
27
29
|
|
28
30
|
bin/lhm-spec-setup-cluster.sh
|
29
31
|
|
30
|
-
|
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
|
-
|
38
|
+
### run the grants
|
37
39
|
|
38
40
|
bin/lhm-spec-grants.sh
|
39
41
|
|
40
42
|
## run specs
|
41
43
|
|
42
|
-
|
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
|
|
data/spec/fixtures/users.ddl
CHANGED
@@ -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
|
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
|
15
|
+
describe 'switching' do
|
16
16
|
before(:each) do
|
17
|
-
@origin = table_create(
|
18
|
-
@destination = table_create(
|
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
|
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
|
28
|
+
table_read(@migration.archive_name).columns.keys.must_include 'origin'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
it
|
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
|
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
|
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
|
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
|
-
|
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
|
15
|
+
describe 'entanglement' do
|
16
16
|
before(:each) do
|
17
|
-
@origin = table_create(
|
18
|
-
@destination = table_create(
|
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
|
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,
|
29
|
+
count(:destination, 'common', 'inserted').must_equal(1)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
it
|
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,
|
41
|
+
count(:destination, 'common', 'inserted').must_equal(0)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
it
|
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,
|
52
|
+
count(:destination, 'common', 'updated').must_equal(1)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
it
|
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,
|
62
|
+
count(:destination, 'common', 'inserted').must_equal(0)
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|