andyjeffries-rubyrep 1.2.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.
- data/History.txt +83 -0
- data/License.txt +20 -0
- data/Manifest.txt +151 -0
- data/README.txt +37 -0
- data/bin/rubyrep +8 -0
- data/lib/rubyrep.rb +72 -0
- data/lib/rubyrep/base_runner.rb +195 -0
- data/lib/rubyrep/command_runner.rb +144 -0
- data/lib/rubyrep/committers/buffered_committer.rb +151 -0
- data/lib/rubyrep/committers/committers.rb +152 -0
- data/lib/rubyrep/configuration.rb +275 -0
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
- data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
- data/lib/rubyrep/database_proxy.rb +52 -0
- data/lib/rubyrep/direct_table_scan.rb +75 -0
- data/lib/rubyrep/generate_runner.rb +105 -0
- data/lib/rubyrep/initializer.rb +39 -0
- data/lib/rubyrep/log_helper.rb +30 -0
- data/lib/rubyrep/logged_change.rb +160 -0
- data/lib/rubyrep/logged_change_loader.rb +197 -0
- data/lib/rubyrep/noisy_connection.rb +80 -0
- data/lib/rubyrep/proxied_table_scan.rb +171 -0
- data/lib/rubyrep/proxy_block_cursor.rb +145 -0
- data/lib/rubyrep/proxy_connection.rb +431 -0
- data/lib/rubyrep/proxy_cursor.rb +44 -0
- data/lib/rubyrep/proxy_row_cursor.rb +43 -0
- data/lib/rubyrep/proxy_runner.rb +89 -0
- data/lib/rubyrep/replication_difference.rb +100 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
- data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
- data/lib/rubyrep/replication_helper.rb +142 -0
- data/lib/rubyrep/replication_initializer.rb +327 -0
- data/lib/rubyrep/replication_run.rb +142 -0
- data/lib/rubyrep/replication_runner.rb +166 -0
- data/lib/rubyrep/replicators/replicators.rb +42 -0
- data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
- data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
- data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
- data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
- data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
- data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
- data/lib/rubyrep/scan_runner.rb +25 -0
- data/lib/rubyrep/session.rb +230 -0
- data/lib/rubyrep/sync_helper.rb +121 -0
- data/lib/rubyrep/sync_runner.rb +31 -0
- data/lib/rubyrep/syncers/syncers.rb +112 -0
- data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
- data/lib/rubyrep/table_scan.rb +54 -0
- data/lib/rubyrep/table_scan_helper.rb +46 -0
- data/lib/rubyrep/table_sorter.rb +70 -0
- data/lib/rubyrep/table_spec_resolver.rb +142 -0
- data/lib/rubyrep/table_sync.rb +90 -0
- data/lib/rubyrep/task_sweeper.rb +77 -0
- data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
- data/lib/rubyrep/type_casting_cursor.rb +31 -0
- data/lib/rubyrep/uninstall_runner.rb +93 -0
- data/lib/rubyrep/version.rb +9 -0
- data/rubyrep +8 -0
- data/rubyrep.bat +4 -0
- data/setup.rb +1585 -0
- data/spec/base_runner_spec.rb +218 -0
- data/spec/buffered_committer_spec.rb +274 -0
- data/spec/command_runner_spec.rb +145 -0
- data/spec/committers_spec.rb +178 -0
- data/spec/configuration_spec.rb +203 -0
- data/spec/connection_extender_interface_spec.rb +141 -0
- data/spec/connection_extenders_registration_spec.rb +164 -0
- data/spec/database_proxy_spec.rb +48 -0
- data/spec/database_rake_spec.rb +40 -0
- data/spec/db_specific_connection_extenders_spec.rb +34 -0
- data/spec/db_specific_replication_extenders_spec.rb +38 -0
- data/spec/direct_table_scan_spec.rb +61 -0
- data/spec/dolphins.jpg +0 -0
- data/spec/generate_runner_spec.rb +84 -0
- data/spec/initializer_spec.rb +46 -0
- data/spec/log_helper_spec.rb +39 -0
- data/spec/logged_change_loader_spec.rb +68 -0
- data/spec/logged_change_spec.rb +470 -0
- data/spec/noisy_connection_spec.rb +78 -0
- data/spec/postgresql_replication_spec.rb +48 -0
- data/spec/postgresql_schema_support_spec.rb +212 -0
- data/spec/postgresql_support_spec.rb +63 -0
- data/spec/progress_bar_spec.rb +77 -0
- data/spec/proxied_table_scan_spec.rb +151 -0
- data/spec/proxy_block_cursor_spec.rb +197 -0
- data/spec/proxy_connection_spec.rb +423 -0
- data/spec/proxy_cursor_spec.rb +56 -0
- data/spec/proxy_row_cursor_spec.rb +66 -0
- data/spec/proxy_runner_spec.rb +70 -0
- data/spec/replication_difference_spec.rb +161 -0
- data/spec/replication_extender_interface_spec.rb +367 -0
- data/spec/replication_extenders_spec.rb +32 -0
- data/spec/replication_helper_spec.rb +178 -0
- data/spec/replication_initializer_spec.rb +509 -0
- data/spec/replication_run_spec.rb +443 -0
- data/spec/replication_runner_spec.rb +254 -0
- data/spec/replicators_spec.rb +36 -0
- data/spec/rubyrep_spec.rb +8 -0
- data/spec/scan_detail_reporter_spec.rb +119 -0
- data/spec/scan_progress_printers_spec.rb +68 -0
- data/spec/scan_report_printers_spec.rb +67 -0
- data/spec/scan_runner_spec.rb +50 -0
- data/spec/scan_summary_reporter_spec.rb +61 -0
- data/spec/session_spec.rb +253 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +305 -0
- data/spec/strange_name_support_spec.rb +135 -0
- data/spec/sync_helper_spec.rb +169 -0
- data/spec/sync_runner_spec.rb +78 -0
- data/spec/syncers_spec.rb +171 -0
- data/spec/table_scan_helper_spec.rb +36 -0
- data/spec/table_scan_spec.rb +49 -0
- data/spec/table_sorter_spec.rb +30 -0
- data/spec/table_spec_resolver_spec.rb +111 -0
- data/spec/table_sync_spec.rb +140 -0
- data/spec/task_sweeper_spec.rb +47 -0
- data/spec/trigger_mode_switcher_spec.rb +83 -0
- data/spec/two_way_replicator_spec.rb +721 -0
- data/spec/two_way_syncer_spec.rb +256 -0
- data/spec/type_casting_cursor_spec.rb +50 -0
- data/spec/uninstall_runner_spec.rb +93 -0
- metadata +190 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe ProxyCursor do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = proxied_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should store session and table and cache the primary keys of table" do
|
|
11
|
+
connection = create_mock_proxy_connection 'dummy_table', ['dummy_key']
|
|
12
|
+
|
|
13
|
+
cursor = ProxyCursor.new connection, 'dummy_table'
|
|
14
|
+
|
|
15
|
+
cursor.connection.should == connection
|
|
16
|
+
cursor.table.should == 'dummy_table'
|
|
17
|
+
cursor.primary_key_names.should == ['dummy_key']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "prepare_fetch should initiate the query and wrap it for type casting" do
|
|
21
|
+
connection = ProxyConnection.new Initializer.configuration.left
|
|
22
|
+
|
|
23
|
+
cursor = ProxyCursor.new(connection, 'scanner_records')
|
|
24
|
+
cursor.prepare_fetch
|
|
25
|
+
cursor.cursor.should be_an_instance_of(TypeCastingCursor)
|
|
26
|
+
cursor.cursor.next_row.should == {'id' => 1, 'name' => 'Alice - exists in both databases'}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "prepare_fetch called with option :row_keys should initiate the correct query" do
|
|
30
|
+
# Note: I am testing row_keys exclusively to make sure that this type of
|
|
31
|
+
# sub query will work correctly on all supported databases
|
|
32
|
+
connection = ProxyConnection.new Initializer.configuration.left
|
|
33
|
+
|
|
34
|
+
cursor = ProxyCursor.new(connection, 'extender_combined_key')
|
|
35
|
+
cursor.prepare_fetch :row_keys => [
|
|
36
|
+
{'first_id' => 1, 'second_id' => 1},
|
|
37
|
+
{'first_id' => 1, 'second_id' => 2}
|
|
38
|
+
]
|
|
39
|
+
cursor.cursor.next_row.should == {'first_id' => 1, 'second_id' => 1, 'name' => 'aa'}
|
|
40
|
+
cursor.cursor.next_row.should == {'first_id' => 1, 'second_id' => 2, 'name' => 'ab'}
|
|
41
|
+
cursor.cursor.next?.should == false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
it "destroy should clear and nil the cursor" do
|
|
46
|
+
connection = create_mock_proxy_connection 'dummy_table', ['dummy_key']
|
|
47
|
+
cursor = ProxyCursor.new connection, 'dummy_table'
|
|
48
|
+
|
|
49
|
+
table_cursor = mock("DBCursor")
|
|
50
|
+
table_cursor.should_receive(:clear)
|
|
51
|
+
cursor.cursor = table_cursor
|
|
52
|
+
|
|
53
|
+
cursor.destroy
|
|
54
|
+
cursor.cursor.should be_nil
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe ProxyRowCursor do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should super to ProxyCursor" do
|
|
11
|
+
session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
|
|
12
|
+
cursor = ProxyRowCursor.new session, 'dummy_table'
|
|
13
|
+
cursor.table.should == 'dummy_table'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "next? should delegate to the DB cursor" do
|
|
17
|
+
session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
|
|
18
|
+
cursor = ProxyRowCursor.new session, 'dummy_table'
|
|
19
|
+
|
|
20
|
+
table_cursor = mock("DBCursor")
|
|
21
|
+
table_cursor.should_receive(:next?).and_return(true)
|
|
22
|
+
cursor.cursor = table_cursor
|
|
23
|
+
|
|
24
|
+
cursor.next?.should == true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "next_row should return the next row in the cursor" do
|
|
28
|
+
session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
|
|
29
|
+
cursor = ProxyRowCursor.new session, 'dummy_table'
|
|
30
|
+
|
|
31
|
+
table_cursor = mock("DBCursor")
|
|
32
|
+
table_cursor.should_receive(:next_row).and_return(:dummy_row)
|
|
33
|
+
cursor.cursor = table_cursor
|
|
34
|
+
|
|
35
|
+
cursor.next_row.should == :dummy_row
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "next_row_keys_and_checksum should store the found row under current_row" do
|
|
39
|
+
session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
|
|
40
|
+
cursor = ProxyRowCursor.new session, 'dummy_table'
|
|
41
|
+
|
|
42
|
+
table_cursor = mock("DBCursor")
|
|
43
|
+
table_cursor.should_receive(:next_row).and_return('dummy_id' => 'dummy_value')
|
|
44
|
+
|
|
45
|
+
cursor.cursor = table_cursor
|
|
46
|
+
cursor.next_row_keys_and_checksum
|
|
47
|
+
cursor.current_row.should == {'dummy_id' => 'dummy_value'}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "next_row_keys_and_checksum should returns the primary_keys and checksum of the found row" do
|
|
51
|
+
session = ProxyConnection.new proxied_config.left
|
|
52
|
+
|
|
53
|
+
cursor = ProxyRowCursor.new session, 'scanner_records'
|
|
54
|
+
cursor.prepare_fetch
|
|
55
|
+
|
|
56
|
+
keys, checksum = cursor.next_row_keys_and_checksum
|
|
57
|
+
|
|
58
|
+
expected_checksum = Digest::SHA1.hexdigest(
|
|
59
|
+
Marshal.dump('id' => 1, 'name' => 'Alice - exists in both databases')
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
keys.should == {'id' => 1}
|
|
63
|
+
checksum.should == expected_checksum
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe ProxyRunner do
|
|
6
|
+
before(:each) do
|
|
7
|
+
DRb.stub!(:start_service)
|
|
8
|
+
DRb.thread.stub!(:join)
|
|
9
|
+
$stderr.stub!(:puts)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "get_options should return options as nil and status as 1 if command line parameters are unknown" do
|
|
13
|
+
# also verify that an error message is printed
|
|
14
|
+
$stderr.should_receive(:puts).any_number_of_times
|
|
15
|
+
options, status = ProxyRunner.new.get_options ["--nonsense"]
|
|
16
|
+
options.should == nil
|
|
17
|
+
status.should == 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "get_options should return options as nil and status as 0 if command line includes '--help'" do
|
|
21
|
+
# also verify that the help message is printed
|
|
22
|
+
$stderr.should_receive(:puts)
|
|
23
|
+
options, status = ProxyRunner.new.get_options ["--help"]
|
|
24
|
+
options.should == nil
|
|
25
|
+
status.should == 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "get_options should return the default options if none were given on the command line" do
|
|
29
|
+
options, status = ProxyRunner.new.get_options []
|
|
30
|
+
options.should == ProxyRunner::DEFAULT_OPTIONS
|
|
31
|
+
status.should == 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "get_options should return :host and :port options as per given command line" do
|
|
35
|
+
options, status = ProxyRunner.new.get_options ["--host", "127.0.0.1", "--port", "1234"]
|
|
36
|
+
options.should == {:host => '127.0.0.1', :port => 1234}
|
|
37
|
+
status.should == 0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "construct_url should create the correct druby URL" do
|
|
41
|
+
ProxyRunner.new.build_url(:host => '127.0.0.1', :port => '1234').should == "druby://127.0.0.1:1234"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "start_server should create a DatabaseProxy and start the DRB server" do
|
|
45
|
+
DatabaseProxy.should_receive(:new)
|
|
46
|
+
DRb.should_receive(:start_service,"druby://127.0.0.1:1234")
|
|
47
|
+
DRb.stub!(:thread).and_return(Object.new)
|
|
48
|
+
DRb.thread.should_receive(:join)
|
|
49
|
+
ProxyRunner.new.start_server("druby://127.0.0.1:1234")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "run should not start a server if the command line is invalid" do
|
|
53
|
+
DRb.should_not_receive(:start_service)
|
|
54
|
+
DRb.stub!(:thread).and_return(Object.new)
|
|
55
|
+
DRb.thread.should_not_receive(:join)
|
|
56
|
+
ProxyRunner.run("--nonsense")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "run should start a server if the command line is correct" do
|
|
60
|
+
DRb.should_receive(:start_service)
|
|
61
|
+
DRb.stub!(:thread).and_return(Object.new)
|
|
62
|
+
DRb.thread.should_receive(:join)
|
|
63
|
+
ProxyRunner.run(["--port=1234"])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should register itself with CommandRunner" do
|
|
67
|
+
CommandRunner.commands['proxy'][:command].should == ProxyRunner
|
|
68
|
+
CommandRunner.commands['proxy'][:description].should be_an_instance_of(String)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe ReplicationDifference do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should store the loaders" do
|
|
11
|
+
session = Session.new
|
|
12
|
+
loaders = LoggedChangeLoaders.new session
|
|
13
|
+
diff = ReplicationDifference.new loaders
|
|
14
|
+
diff.loaders.should == loaders
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "loaded? should return true if a difference was loaded" do
|
|
18
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
|
|
19
|
+
diff.should_not be_loaded
|
|
20
|
+
diff.loaded = true
|
|
21
|
+
diff.should be_loaded
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "load should leave the instance unloaded if no changes are available" do
|
|
25
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
|
|
26
|
+
diff.load
|
|
27
|
+
diff.should_not be_loaded
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "load should load left differences successfully" do
|
|
31
|
+
session = Session.new
|
|
32
|
+
session.left.begin_db_transaction
|
|
33
|
+
begin
|
|
34
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
35
|
+
'change_table' => 'scanner_records',
|
|
36
|
+
'change_key' => 'id|1',
|
|
37
|
+
'change_type' => 'I',
|
|
38
|
+
'change_time' => Time.now
|
|
39
|
+
}
|
|
40
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
41
|
+
diff.load
|
|
42
|
+
|
|
43
|
+
diff.should be_loaded
|
|
44
|
+
diff.type.should == :left
|
|
45
|
+
diff.changes[:left].key.should == {'id' => '1'}
|
|
46
|
+
ensure
|
|
47
|
+
session.left.rollback_db_transaction
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "load should load right differences successfully" do
|
|
52
|
+
session = Session.new
|
|
53
|
+
session.right.begin_db_transaction
|
|
54
|
+
begin
|
|
55
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
56
|
+
'change_table' => 'scanner_records',
|
|
57
|
+
'change_key' => 'id|1',
|
|
58
|
+
'change_type' => 'D',
|
|
59
|
+
'change_time' => Time.now
|
|
60
|
+
}
|
|
61
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
62
|
+
diff.load
|
|
63
|
+
|
|
64
|
+
diff.should be_loaded
|
|
65
|
+
diff.type.should == :right
|
|
66
|
+
diff.changes[:right].key.should == {'id' => '1'}
|
|
67
|
+
ensure
|
|
68
|
+
session.right.rollback_db_transaction
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "load should load conflict differences successfully" do
|
|
73
|
+
config = deep_copy(standard_config)
|
|
74
|
+
config.included_table_specs.clear
|
|
75
|
+
config.include_tables "table_with_manual_key, extender_without_key"
|
|
76
|
+
|
|
77
|
+
session = Session.new config
|
|
78
|
+
session.left.begin_db_transaction
|
|
79
|
+
session.right.begin_db_transaction
|
|
80
|
+
begin
|
|
81
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
82
|
+
'change_table' => 'dummy_table',
|
|
83
|
+
'change_key' => 'id|2',
|
|
84
|
+
'change_type' => 'I',
|
|
85
|
+
'change_time' => Time.now
|
|
86
|
+
}
|
|
87
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
88
|
+
'change_table' => 'table_with_manual_key',
|
|
89
|
+
'change_key' => 'id|1',
|
|
90
|
+
'change_new_key' => 'id|1',
|
|
91
|
+
'change_type' => 'U',
|
|
92
|
+
'change_time' => 5.seconds.from_now
|
|
93
|
+
}
|
|
94
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
95
|
+
'change_table' => 'extender_without_key',
|
|
96
|
+
'change_key' => 'id|1',
|
|
97
|
+
'change_type' => 'D',
|
|
98
|
+
'change_time' => 5.seconds.ago
|
|
99
|
+
}
|
|
100
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
101
|
+
diff.load
|
|
102
|
+
|
|
103
|
+
diff.should be_loaded
|
|
104
|
+
diff.type.should == :conflict
|
|
105
|
+
diff.changes[:left].type.should == :update
|
|
106
|
+
diff.changes[:left].table.should == 'table_with_manual_key'
|
|
107
|
+
diff.changes[:left].key.should == {'id' => '1'}
|
|
108
|
+
diff.changes[:right].type.should == :delete
|
|
109
|
+
diff.changes[:right].table.should == 'extender_without_key'
|
|
110
|
+
diff.changes[:right].key.should == {'id' => '1'}
|
|
111
|
+
ensure
|
|
112
|
+
session.left.rollback_db_transaction
|
|
113
|
+
session.right.rollback_db_transaction
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "amend should amend the replication difference with new found changes" do
|
|
118
|
+
session = Session.new
|
|
119
|
+
session.left.begin_db_transaction
|
|
120
|
+
session.right.begin_db_transaction
|
|
121
|
+
begin
|
|
122
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
123
|
+
'change_table' => 'scanner_records',
|
|
124
|
+
'change_key' => 'id|1',
|
|
125
|
+
'change_type' => 'I',
|
|
126
|
+
'change_time' => Time.now
|
|
127
|
+
}
|
|
128
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
129
|
+
diff.load
|
|
130
|
+
|
|
131
|
+
diff.should be_loaded
|
|
132
|
+
diff.type.should == :right
|
|
133
|
+
diff.changes[:right].key.should == {'id' => '1'}
|
|
134
|
+
|
|
135
|
+
# if there are no changes, the diff should still be the same
|
|
136
|
+
diff.amend
|
|
137
|
+
diff.type.should == :right
|
|
138
|
+
diff.changes[:right].key.should == {'id' => '1'}
|
|
139
|
+
|
|
140
|
+
# should recognize new changes
|
|
141
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
142
|
+
'change_table' => 'scanner_records',
|
|
143
|
+
'change_key' => 'id|1',
|
|
144
|
+
'change_type' => 'D',
|
|
145
|
+
'change_time' => Time.now
|
|
146
|
+
}
|
|
147
|
+
diff.amend
|
|
148
|
+
diff.type.should == :conflict
|
|
149
|
+
diff.changes[:left].key.should == {'id' => '1'}
|
|
150
|
+
diff.changes[:right].key.should == {'id' => '1'}
|
|
151
|
+
ensure
|
|
152
|
+
session.right.rollback_db_transaction
|
|
153
|
+
session.left.rollback_db_transaction
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "to_yaml should blank out session" do
|
|
158
|
+
diff = ReplicationDifference.new :dummy_session
|
|
159
|
+
diff.to_yaml.should_not =~ /session/
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
include RR
|
|
5
|
+
|
|
6
|
+
# All ReplicationExtenders need to pass this spec
|
|
7
|
+
describe "ReplicationExtender", :shared => true do
|
|
8
|
+
before(:each) do
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "create_replication_trigger created triggers should log data changes" do
|
|
12
|
+
session = nil
|
|
13
|
+
begin
|
|
14
|
+
session = Session.new
|
|
15
|
+
session.left.begin_db_transaction
|
|
16
|
+
params = {
|
|
17
|
+
:trigger_name => 'rr_trigger_test',
|
|
18
|
+
:table => 'trigger_test',
|
|
19
|
+
:keys => ['first_id', 'second_id'],
|
|
20
|
+
:log_table => 'rr_pending_changes',
|
|
21
|
+
:key_sep => '|',
|
|
22
|
+
:exclude_rr_activity => false,
|
|
23
|
+
}
|
|
24
|
+
session.left.create_replication_trigger params
|
|
25
|
+
|
|
26
|
+
change_start = Time.now
|
|
27
|
+
|
|
28
|
+
session.left.insert_record 'trigger_test', {
|
|
29
|
+
'first_id' => 1,
|
|
30
|
+
'second_id' => 2,
|
|
31
|
+
'name' => 'bla'
|
|
32
|
+
}
|
|
33
|
+
session.left.execute "update trigger_test set second_id = 9 where first_id = 1 and second_id = 2"
|
|
34
|
+
session.left.delete_record 'trigger_test', {
|
|
35
|
+
'first_id' => 1,
|
|
36
|
+
'second_id' => 9,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
|
|
40
|
+
|
|
41
|
+
# Verify that the timestamps are created correctly
|
|
42
|
+
rows.each do |row|
|
|
43
|
+
Time.parse(row['change_time']).to_i >= change_start.to_i
|
|
44
|
+
Time.parse(row['change_time']).to_i <= Time.now.to_i
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
48
|
+
rows.should == [
|
|
49
|
+
{'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|2', 'change_new_key' => nil, 'change_type' => 'I'},
|
|
50
|
+
{'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|2', 'change_new_key' => 'first_id|1|second_id|9', 'change_type' => 'U'},
|
|
51
|
+
{'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|9', 'change_new_key' => nil, 'change_type' => 'D'},
|
|
52
|
+
]
|
|
53
|
+
ensure
|
|
54
|
+
session.left.execute 'delete from trigger_test' if session
|
|
55
|
+
session.left.execute 'delete from rr_pending_changes' if session
|
|
56
|
+
session.left.rollback_db_transaction if session
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "created triggers should not log rubyrep initiated changes if :exclude_rubyrep_activity is true" do
|
|
61
|
+
session = nil
|
|
62
|
+
begin
|
|
63
|
+
session = Session.new
|
|
64
|
+
session.left.begin_db_transaction
|
|
65
|
+
params = {
|
|
66
|
+
:trigger_name => 'rr_trigger_test',
|
|
67
|
+
:table => 'trigger_test',
|
|
68
|
+
:keys => ['first_id', 'second_id'],
|
|
69
|
+
:log_table => 'rr_pending_changes',
|
|
70
|
+
:key_sep => '|',
|
|
71
|
+
:exclude_rr_activity => true,
|
|
72
|
+
:activity_table => "rr_running_flags",
|
|
73
|
+
}
|
|
74
|
+
session.left.create_replication_trigger params
|
|
75
|
+
|
|
76
|
+
session.left.insert_record 'rr_running_flags', {
|
|
77
|
+
'active' => 1
|
|
78
|
+
}
|
|
79
|
+
session.left.insert_record 'trigger_test', {
|
|
80
|
+
'first_id' => 1,
|
|
81
|
+
'second_id' => 2,
|
|
82
|
+
'name' => 'bla'
|
|
83
|
+
}
|
|
84
|
+
session.left.connection.execute('delete from rr_running_flags')
|
|
85
|
+
session.left.insert_record 'trigger_test', {
|
|
86
|
+
'first_id' => 1,
|
|
87
|
+
'second_id' => 3,
|
|
88
|
+
'name' => 'bla'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
|
|
92
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
93
|
+
rows.should == [{
|
|
94
|
+
'change_table' => 'trigger_test',
|
|
95
|
+
'change_key' => 'first_id|1|second_id|3',
|
|
96
|
+
'change_new_key' => nil,
|
|
97
|
+
'change_type' => 'I'
|
|
98
|
+
}]
|
|
99
|
+
ensure
|
|
100
|
+
session.left.execute 'delete from trigger_test' if session
|
|
101
|
+
session.left.execute 'delete from rr_pending_changes' if session
|
|
102
|
+
session.left.rollback_db_transaction if session
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "created triggers should work with tables having non-combined primary keys" do
|
|
107
|
+
session = nil
|
|
108
|
+
begin
|
|
109
|
+
session = Session.new
|
|
110
|
+
session.left.begin_db_transaction
|
|
111
|
+
params = {
|
|
112
|
+
:trigger_name => 'rr_extender_no_record',
|
|
113
|
+
:table => 'extender_no_record',
|
|
114
|
+
:keys => ['id'],
|
|
115
|
+
:log_table => 'rr_pending_changes',
|
|
116
|
+
:key_sep => '|',
|
|
117
|
+
}
|
|
118
|
+
session.left.create_replication_trigger params
|
|
119
|
+
session.left.insert_record 'extender_no_record', {
|
|
120
|
+
'id' => 9,
|
|
121
|
+
'name' => 'bla'
|
|
122
|
+
}
|
|
123
|
+
rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
|
|
124
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
125
|
+
rows.should == [{
|
|
126
|
+
'change_table' => 'extender_no_record',
|
|
127
|
+
'change_key' => 'id|9',
|
|
128
|
+
'change_new_key' => nil,
|
|
129
|
+
'change_type' => 'I'
|
|
130
|
+
}]
|
|
131
|
+
ensure
|
|
132
|
+
if session
|
|
133
|
+
session.left.execute 'delete from extender_no_record'
|
|
134
|
+
session.left.execute 'delete from rr_pending_changes'
|
|
135
|
+
session.left.drop_replication_trigger('rr_extender_no_record', 'extender_no_record')
|
|
136
|
+
session.left.rollback_db_transaction
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "created triggers should work with primary keys holding multi-byte text values" do
|
|
142
|
+
session = nil
|
|
143
|
+
begin
|
|
144
|
+
session = Session.new
|
|
145
|
+
session.left.begin_db_transaction
|
|
146
|
+
params = {
|
|
147
|
+
:trigger_name => 'rr_scanner_text_key',
|
|
148
|
+
:table => 'scanner_text_key',
|
|
149
|
+
:keys => ['text_id'],
|
|
150
|
+
:log_table => 'rr_pending_changes',
|
|
151
|
+
:key_sep => '|',
|
|
152
|
+
}
|
|
153
|
+
session.left.create_replication_trigger params
|
|
154
|
+
session.left.insert_record 'scanner_text_key', {
|
|
155
|
+
'text_id' => 'よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.',
|
|
156
|
+
'name' => 'bla'
|
|
157
|
+
}
|
|
158
|
+
rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
|
|
159
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
160
|
+
rows.should == [{
|
|
161
|
+
'change_table' => 'scanner_text_key',
|
|
162
|
+
'change_key' => 'text_id|よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.',
|
|
163
|
+
'change_new_key' => nil,
|
|
164
|
+
'change_type' => 'I'
|
|
165
|
+
}]
|
|
166
|
+
found_key = rows[0]['change_key'].sub(/^text_id\|/, '')
|
|
167
|
+
session.left.
|
|
168
|
+
select_one("select * from scanner_text_key where text_id = '#{found_key}'").
|
|
169
|
+
should_not be_nil
|
|
170
|
+
ensure
|
|
171
|
+
if session
|
|
172
|
+
session.left.execute "delete from scanner_text_key where text_id = 'よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.'"
|
|
173
|
+
session.left.execute 'delete from rr_pending_changes'
|
|
174
|
+
session.left.drop_replication_trigger('rr_scanner_text_key', 'scanner_text_key')
|
|
175
|
+
session.left.rollback_db_transaction
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "replication_trigger_exists? and drop_replication_trigger should work correctly" do
|
|
181
|
+
session = nil
|
|
182
|
+
begin
|
|
183
|
+
session = Session.new
|
|
184
|
+
if session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test')
|
|
185
|
+
session.left.drop_replication_trigger('rr_trigger_test', 'trigger_test')
|
|
186
|
+
end
|
|
187
|
+
session.left.begin_db_transaction
|
|
188
|
+
params = {
|
|
189
|
+
:trigger_name => 'rr_trigger_test',
|
|
190
|
+
:table => 'trigger_test',
|
|
191
|
+
:keys => ['first_id'],
|
|
192
|
+
:log_table => 'rr_pending_changes',
|
|
193
|
+
:key_sep => '|',
|
|
194
|
+
}
|
|
195
|
+
session.left.create_replication_trigger params
|
|
196
|
+
|
|
197
|
+
session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test').
|
|
198
|
+
should be_true
|
|
199
|
+
session.left.drop_replication_trigger('rr_trigger_test', 'trigger_test')
|
|
200
|
+
session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test').
|
|
201
|
+
should be_false
|
|
202
|
+
ensure
|
|
203
|
+
session.left.rollback_db_transaction if session
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "sequence_values should return an empty hash if table has no sequences" do
|
|
208
|
+
session = Session.new
|
|
209
|
+
session.left.sequence_values('rr', 'scanner_text_key').
|
|
210
|
+
should == {}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "sequence_values should return the correct sequence settings" do
|
|
214
|
+
session = nil
|
|
215
|
+
begin
|
|
216
|
+
session = Session.new
|
|
217
|
+
session.left.begin_db_transaction
|
|
218
|
+
session.left.execute 'delete from sequence_test'
|
|
219
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
220
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
221
|
+
session.left.update_sequences \
|
|
222
|
+
'rr', 'sequence_test', 5, 2,
|
|
223
|
+
left_sequence_values, right_sequence_values, 5
|
|
224
|
+
sequence_value = session.left.sequence_values('rr', 'sequence_test').values[0]
|
|
225
|
+
sequence_value[:increment].should == 5
|
|
226
|
+
(sequence_value[:value] % 5).should == 2
|
|
227
|
+
ensure
|
|
228
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
229
|
+
session.left.execute "delete from sequence_test" if session
|
|
230
|
+
session.left.rollback_db_transaction if session
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "update_sequences should ensure that a table's auto generated ID values have the correct increment and offset" do
|
|
235
|
+
session = nil
|
|
236
|
+
begin
|
|
237
|
+
session = Session.new
|
|
238
|
+
session.left.begin_db_transaction
|
|
239
|
+
|
|
240
|
+
# Note:
|
|
241
|
+
# Calling ensure_sequence_setup twice with different values to ensure that
|
|
242
|
+
# it is actually does something.
|
|
243
|
+
|
|
244
|
+
session.left.execute 'delete from sequence_test'
|
|
245
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
246
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
247
|
+
session.left.update_sequences \
|
|
248
|
+
'rr', 'sequence_test', 1, 0,
|
|
249
|
+
left_sequence_values, right_sequence_values, 5
|
|
250
|
+
id1, id2 = get_example_sequence_values(session)
|
|
251
|
+
(id2 - id1).should == 1
|
|
252
|
+
|
|
253
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
254
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
255
|
+
session.left.update_sequences \
|
|
256
|
+
'rr', 'sequence_test', 5, 2,
|
|
257
|
+
left_sequence_values, right_sequence_values, 5
|
|
258
|
+
id1, id2 = get_example_sequence_values(session)
|
|
259
|
+
(id2 - id1).should == 5
|
|
260
|
+
(id1 % 5).should == 2
|
|
261
|
+
ensure
|
|
262
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
263
|
+
session.left.execute "delete from sequence_test" if session
|
|
264
|
+
session.left.rollback_db_transaction if session
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "update_sequences shoud set the sequence up correctly if the table is not empty" do
|
|
269
|
+
session = nil
|
|
270
|
+
begin
|
|
271
|
+
session = Session.new
|
|
272
|
+
session.left.begin_db_transaction
|
|
273
|
+
session.left.execute 'delete from sequence_test'
|
|
274
|
+
session.left.insert_record 'sequence_test', { 'name' => 'whatever' }
|
|
275
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
276
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
277
|
+
session.left.update_sequences \
|
|
278
|
+
'rr', 'sequence_test', 2, 0,
|
|
279
|
+
left_sequence_values, right_sequence_values, 5
|
|
280
|
+
id1, id2 = get_example_sequence_values(session)
|
|
281
|
+
(id2 - id1).should == 2
|
|
282
|
+
ensure
|
|
283
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
284
|
+
session.left.execute "delete from sequence_test" if session
|
|
285
|
+
session.left.rollback_db_transaction if session
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "update_sequences shoud set the sequence up correctly if the other table is already set up correctly" do
|
|
290
|
+
session = nil
|
|
291
|
+
begin
|
|
292
|
+
session = Session.new
|
|
293
|
+
session.left.begin_db_transaction
|
|
294
|
+
session.right.begin_db_transaction
|
|
295
|
+
session.left.execute 'delete from sequence_test'
|
|
296
|
+
session.left.insert_record 'sequence_test', { 'name' => 'whatever' }
|
|
297
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
298
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
299
|
+
|
|
300
|
+
# setup right table sequence
|
|
301
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
302
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
303
|
+
session.right.update_sequences \
|
|
304
|
+
'rr', 'sequence_test', 2, 1,
|
|
305
|
+
left_sequence_values, right_sequence_values, 5
|
|
306
|
+
|
|
307
|
+
# now setup left table sequence and verify result
|
|
308
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
309
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
310
|
+
session.left.update_sequences \
|
|
311
|
+
'rr', 'sequence_test', 2, 0,
|
|
312
|
+
left_sequence_values, right_sequence_values, 5
|
|
313
|
+
id1, id2 = get_example_sequence_values(session)
|
|
314
|
+
(id2 - id1).should == 2
|
|
315
|
+
ensure
|
|
316
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
317
|
+
session.right.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
318
|
+
session.left.execute "delete from sequence_test" if session
|
|
319
|
+
session.left.rollback_db_transaction if session
|
|
320
|
+
session.right.rollback_db_transaction if session
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it "clear_sequence_setup should remove custom sequence settings" do
|
|
325
|
+
session = nil
|
|
326
|
+
begin
|
|
327
|
+
session = Session.new
|
|
328
|
+
session.left.begin_db_transaction
|
|
329
|
+
left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
|
|
330
|
+
right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
|
|
331
|
+
session.left.update_sequences \
|
|
332
|
+
'rr', 'sequence_test', 2, 0,
|
|
333
|
+
left_sequence_values, right_sequence_values, 5
|
|
334
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test'
|
|
335
|
+
id1, id2 = get_example_sequence_values(session)
|
|
336
|
+
(id2 - id1).should == 1
|
|
337
|
+
ensure
|
|
338
|
+
session.left.clear_sequence_setup 'rr', 'sequence_test' if session
|
|
339
|
+
session.left.execute "delete from sequence_test" if session
|
|
340
|
+
session.left.rollback_db_transaction if session
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "add_big_primary_key should add a 8 byte, auto incrementing primary key" do
|
|
345
|
+
session = nil
|
|
346
|
+
begin
|
|
347
|
+
session = Session.new
|
|
348
|
+
initializer = ReplicationInitializer.new(session)
|
|
349
|
+
initializer.silence_ddl_notices(:left) do
|
|
350
|
+
session.left.drop_table 'big_key_test' if session.left.tables.include? 'big_key_test'
|
|
351
|
+
session.left.create_table 'big_key_test'.to_sym
|
|
352
|
+
session.left.add_column 'big_key_test', :name, :string
|
|
353
|
+
session.left.remove_column 'big_key_test', 'id'
|
|
354
|
+
session.left.add_big_primary_key 'big_key_test', 'id'
|
|
355
|
+
end
|
|
356
|
+
# should auto generate the primary key if not manually specified
|
|
357
|
+
session.left.insert_record 'big_key_test', {'name' => 'bla'}
|
|
358
|
+
session.left.select_one("select id from big_key_test where name = 'bla'")['id'].
|
|
359
|
+
to_i.should > 0
|
|
360
|
+
|
|
361
|
+
# should allow 8 byte values
|
|
362
|
+
session.left.insert_record 'big_key_test', {'id' => 1e18.to_i, 'name' => 'blub'}
|
|
363
|
+
session.left.select_one("select id from big_key_test where name = 'blub'")['id'].
|
|
364
|
+
to_i.should == 1e18.to_i
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|