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,140 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableSync do
|
|
6
|
+
it "sync_options should return the correct table specific sync options" do
|
|
7
|
+
config = deep_copy(standard_config)
|
|
8
|
+
old_table_specific_options = config.tables_with_options
|
|
9
|
+
begin
|
|
10
|
+
config.options = {:syncer => :bla}
|
|
11
|
+
config.include_tables 'scanner_records', {:syncer => :blub}
|
|
12
|
+
TableSync.new(Session.new(config), 'scanner_records').sync_options[:syncer] \
|
|
13
|
+
.should == :blub
|
|
14
|
+
ensure
|
|
15
|
+
config.instance_eval {@tables_with_options = old_table_specific_options}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "execute_sync_hook should work if the hook is not configured" do
|
|
20
|
+
session = Session.new standard_config
|
|
21
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
22
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "execute_sync_hook should execute the given SQL command" do
|
|
26
|
+
config = deep_copy(standard_config)
|
|
27
|
+
config.add_table_options 'scanner_records', :before_table_sync => 'dummy_command'
|
|
28
|
+
session = Session.new config
|
|
29
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
30
|
+
|
|
31
|
+
session.left.should_receive(:execute).with('dummy_command')
|
|
32
|
+
session.right.should_receive(:execute).with('dummy_command')
|
|
33
|
+
|
|
34
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "execute_sync_hook should execute the given Proc" do
|
|
38
|
+
config = deep_copy(standard_config)
|
|
39
|
+
received_handler = nil
|
|
40
|
+
config.add_table_options 'scanner_records',
|
|
41
|
+
:before_table_sync => lambda {|helper| received_handler = helper}
|
|
42
|
+
session = Session.new config
|
|
43
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
44
|
+
sync.helper = :dummy_helper
|
|
45
|
+
|
|
46
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
47
|
+
|
|
48
|
+
received_handler.should == :dummy_helper
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "event_filtered? should return false if there is no event filter" do
|
|
52
|
+
session = Session.new standard_config
|
|
53
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
54
|
+
|
|
55
|
+
sync.event_filtered?(:left, 'id' => 1).should be_false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "event_filtered? should return false if event filter does not filter sync events" do
|
|
59
|
+
config = deep_copy(standard_config)
|
|
60
|
+
config.add_table_options 'scanner_records', :event_filter => Object.new
|
|
61
|
+
session = Session.new config
|
|
62
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
63
|
+
|
|
64
|
+
sync.event_filtered?(:left, 'id' => 1).should be_false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "event_filtered? should signal filtering (i. e. return true) if the event filter result is false" do
|
|
68
|
+
filter = Object.new
|
|
69
|
+
def filter.before_sync(table, key, helper, type, row)
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
config = deep_copy(standard_config)
|
|
73
|
+
config.add_table_options 'scanner_records', :event_filter => filter
|
|
74
|
+
session = Session.new config
|
|
75
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
76
|
+
sync.helper = SyncHelper.new(sync)
|
|
77
|
+
sync.event_filtered?(:left, 'id' => 1).should be_true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "event_filtered? should return false if the event filter result is true" do
|
|
81
|
+
filter = {}
|
|
82
|
+
def filter.before_sync(table, key, helper, type, row)
|
|
83
|
+
self[:args] = [table, key, helper, type, row]
|
|
84
|
+
true
|
|
85
|
+
end
|
|
86
|
+
config = deep_copy(standard_config)
|
|
87
|
+
config.add_table_options 'scanner_records', :event_filter => filter
|
|
88
|
+
session = Session.new config
|
|
89
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
90
|
+
sync.helper = SyncHelper.new(sync)
|
|
91
|
+
sync.event_filtered?(:left, 'id' => 1, 'name' => 'bla').should be_false
|
|
92
|
+
|
|
93
|
+
# verify correct parameter assignment
|
|
94
|
+
filter[:args].should == ['scanner_records', {'id' => 1}, sync.helper, :left, {'id' => 1, 'name' => 'bla'}]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "run should synchronize the databases" do
|
|
98
|
+
config = deep_copy(standard_config)
|
|
99
|
+
config.options[:committer] = :never_commit
|
|
100
|
+
config.options[:logged_sync_events] = [:all_conflicts]
|
|
101
|
+
before_hook_called = false
|
|
102
|
+
after_hook_called = false
|
|
103
|
+
config.options[:before_table_sync] = lambda {|helper| before_hook_called = true}
|
|
104
|
+
config.options[:after_table_sync] = lambda { |helper| after_hook_called = true}
|
|
105
|
+
|
|
106
|
+
filter = Object.new
|
|
107
|
+
def filter.before_sync(table, key, helper, type, row)
|
|
108
|
+
key['id'] != 6
|
|
109
|
+
end
|
|
110
|
+
config.options[:event_filter] = filter
|
|
111
|
+
session = Session.new(config)
|
|
112
|
+
begin
|
|
113
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
114
|
+
sync.run
|
|
115
|
+
|
|
116
|
+
# Verify that sync events are logged
|
|
117
|
+
row = session.left.select_one("select * from rr_logged_events where change_key = '2' order by id")
|
|
118
|
+
row['change_table'].should == 'scanner_records'
|
|
119
|
+
row['diff_type'].should == 'conflict'
|
|
120
|
+
row['description'].should == 'left_wins'
|
|
121
|
+
|
|
122
|
+
# verify that the table was synchronized
|
|
123
|
+
left_records = session.left.select_all("select * from scanner_records where id <> 6 order by id")
|
|
124
|
+
right_records = session.right.select_all("select * from scanner_records where id <> 6 order by id")
|
|
125
|
+
left_records.should == right_records
|
|
126
|
+
|
|
127
|
+
# verify that the filtered out record was not synced
|
|
128
|
+
session.left.select_one("select * from scanner_records where id = 6").
|
|
129
|
+
should be_nil
|
|
130
|
+
|
|
131
|
+
# verify that hooks where called
|
|
132
|
+
before_hook_called.should be_true
|
|
133
|
+
after_hook_called.should be_true
|
|
134
|
+
ensure
|
|
135
|
+
Committers::NeverCommitter.rollback_current_session
|
|
136
|
+
session.left.execute "delete from rr_logged_events"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TaskSweeper do
|
|
6
|
+
before(:each) do
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should execute the given task" do
|
|
10
|
+
x = nil
|
|
11
|
+
TaskSweeper.timeout(1) {|sweeper| x = 1}
|
|
12
|
+
x.should == 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should raise exceptions thrown by the task" do
|
|
16
|
+
lambda {
|
|
17
|
+
TaskSweeper.timeout(1) {raise "bla"}
|
|
18
|
+
}.should raise_error("bla")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should return if task stalls" do
|
|
22
|
+
start = Time.now
|
|
23
|
+
TaskSweeper.timeout(0.01) {sleep 10}.should be_terminated
|
|
24
|
+
(Time.now - start < 5).should be_true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should not return if task is active" do
|
|
28
|
+
start = Time.now
|
|
29
|
+
TaskSweeper.timeout(0.1) do |sweeper|
|
|
30
|
+
10.times do
|
|
31
|
+
sleep 0.05
|
|
32
|
+
sweeper.ping
|
|
33
|
+
end
|
|
34
|
+
end.should_not be_terminated
|
|
35
|
+
(Time.now - start > 0.4).should be_true
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should notify a stalled task about it's termination" do
|
|
40
|
+
terminated = false
|
|
41
|
+
TaskSweeper.timeout(0.01) do |sweeper|
|
|
42
|
+
sleep 0.05
|
|
43
|
+
terminated = sweeper.terminated?
|
|
44
|
+
end.join
|
|
45
|
+
terminated.should be_true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TriggerModeSwitcher do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should save the session and initialize triggers hash" do
|
|
11
|
+
session = Session.new
|
|
12
|
+
switcher = TriggerModeSwitcher.new(session)
|
|
13
|
+
switcher.session.should == session
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "exclude_rr_activity should switch the trigger for the given table" do
|
|
17
|
+
switcher = TriggerModeSwitcher.new(Session.new)
|
|
18
|
+
|
|
19
|
+
switcher.should_receive(:switch_trigger_mode).with(:right, 'right1', true).once
|
|
20
|
+
switcher.exclude_rr_activity(:right, 'right1')
|
|
21
|
+
|
|
22
|
+
# Verify that for a given table, the trigger is not modified multiple times
|
|
23
|
+
switcher.exclude_rr_activity(:right, 'right1')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "restore_triggers should restore the triggers" do
|
|
27
|
+
switcher = TriggerModeSwitcher.new(Session.new)
|
|
28
|
+
|
|
29
|
+
switcher.stub!(:switch_trigger_mode)
|
|
30
|
+
switcher.exclude_rr_activity :left, 'left1'
|
|
31
|
+
switcher.should_receive(:switch_trigger_mode).with(:left, 'left1', false).once
|
|
32
|
+
switcher.restore_triggers
|
|
33
|
+
switcher.restore_triggers # ensure the restore is only done once
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "switch_trigger_mode should switch the exclude_rr_activity mode as specified" do
|
|
37
|
+
session = nil
|
|
38
|
+
initializer = nil
|
|
39
|
+
begin
|
|
40
|
+
session = Session.new
|
|
41
|
+
initializer = ReplicationInitializer.new(session)
|
|
42
|
+
initializer.create_trigger(:left, 'trigger_test')
|
|
43
|
+
|
|
44
|
+
switcher = TriggerModeSwitcher.new session
|
|
45
|
+
switcher.switch_trigger_mode :left, 'trigger_test', true
|
|
46
|
+
|
|
47
|
+
session.left.insert_record 'trigger_test', {
|
|
48
|
+
'first_id' => 1,
|
|
49
|
+
'second_id' => 2,
|
|
50
|
+
'name' => 'blub'
|
|
51
|
+
}
|
|
52
|
+
session.left.execute "insert into rr_running_flags values(1)"
|
|
53
|
+
session.left.insert_record 'trigger_test', {
|
|
54
|
+
'first_id' => 1,
|
|
55
|
+
'second_id' => 3,
|
|
56
|
+
'name' => 'bla'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
rows = session.left.select_all("select * from rr_pending_changes order by id")
|
|
60
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
61
|
+
rows.should == [{
|
|
62
|
+
'change_table' => 'trigger_test',
|
|
63
|
+
'change_key' => 'first_id|1|second_id|2',
|
|
64
|
+
'change_new_key' => nil,
|
|
65
|
+
'change_type' => 'I'
|
|
66
|
+
}]
|
|
67
|
+
ensure
|
|
68
|
+
initializer.drop_trigger :left, 'trigger_test' if initializer
|
|
69
|
+
if session
|
|
70
|
+
session.left.execute 'delete from rr_running_flags'
|
|
71
|
+
session.left.execute 'delete from trigger_test'
|
|
72
|
+
session.left.execute 'delete from rr_pending_changes'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "switch_trigger_mode should not switch the trigger mode if the table has no trigger" do
|
|
78
|
+
session = Session.new
|
|
79
|
+
switcher = TriggerModeSwitcher.new session
|
|
80
|
+
session.left.should_not_receive(:execute)
|
|
81
|
+
switcher.switch_trigger_mode(:left, 'scanner_records', true)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe Replicators::TwoWayReplicator do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = deep_copy(standard_config)
|
|
8
|
+
Initializer.configuration.options = {:replicator => :two_way}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should register itself" do
|
|
12
|
+
Replicators::replicators[:two_way].should == Replicators::TwoWayReplicator
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "initialize should store the replication helper" do
|
|
16
|
+
rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
|
|
17
|
+
helper = ReplicationHelper.new(rep_run)
|
|
18
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
19
|
+
replicator.rep_helper.should == helper
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "verify_option should raise descriptive errors" do
|
|
23
|
+
rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
|
|
24
|
+
helper = ReplicationHelper.new(rep_run)
|
|
25
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
26
|
+
lambda {replicator.verify_option(nil, [:valid_value], :my_key, :my_value)}.
|
|
27
|
+
should raise_error(ArgumentError, ':my_value not a valid :my_key option')
|
|
28
|
+
lambda {replicator.verify_option(/my_spec/, [:valid_value], :my_key, :my_value)}.
|
|
29
|
+
should raise_error(ArgumentError, '/my_spec/: :my_value not a valid :my_key option')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "initialize should throw an error if options are invalid" do
|
|
33
|
+
rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
|
|
34
|
+
helper = ReplicationHelper.new(rep_run)
|
|
35
|
+
base_options = {
|
|
36
|
+
:replicator => :two_way,
|
|
37
|
+
:left_change_handling => :ignore,
|
|
38
|
+
:right_change_handling => :ignore,
|
|
39
|
+
:replication_conflict_handling => :ignore,
|
|
40
|
+
:logged_replication_events => [:ignored_conflicts]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Verify that correct options don't raise errors.
|
|
44
|
+
helper.stub!(:options).and_return(base_options)
|
|
45
|
+
lambda {Replicators::TwoWayReplicator.new(helper)}.should_not raise_error
|
|
46
|
+
|
|
47
|
+
# Also lambda options should not raise errors.
|
|
48
|
+
l = lambda {}
|
|
49
|
+
helper.stub!(:options).and_return(base_options.merge(
|
|
50
|
+
{
|
|
51
|
+
:left_change_handling => l,
|
|
52
|
+
:right_change_handling => l,
|
|
53
|
+
:repliction_conflict_handling => l
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
lambda {Replicators::TwoWayReplicator.new(helper)}.should_not raise_error
|
|
57
|
+
|
|
58
|
+
# Invalid options should raise errors
|
|
59
|
+
invalid_options = [
|
|
60
|
+
{:left_change_handling => :invalid_left_option},
|
|
61
|
+
{:right_change_handling => :invalid_right_option},
|
|
62
|
+
{:replication_conflict_handling => :invalid_conflict_option},
|
|
63
|
+
{:logged_replication_events => :invalid_logging_option},
|
|
64
|
+
]
|
|
65
|
+
invalid_options.each do |options|
|
|
66
|
+
helper.session.configuration.stub!(:options).and_return(base_options.merge(options))
|
|
67
|
+
lambda {Replicators::TwoWayReplicator.new(helper)}.should raise_error(ArgumentError)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "clear_conflicts should update the correct database with the correct action" do
|
|
72
|
+
Initializer.configuration.include_tables 'left_table, right_table'
|
|
73
|
+
session = Session.new
|
|
74
|
+
session.left.begin_db_transaction
|
|
75
|
+
session.right.begin_db_transaction
|
|
76
|
+
begin
|
|
77
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
78
|
+
helper = ReplicationHelper.new(rep_run)
|
|
79
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
80
|
+
|
|
81
|
+
loaders = LoggedChangeLoaders.new(session)
|
|
82
|
+
|
|
83
|
+
left_change = LoggedChange.new loaders[:left]
|
|
84
|
+
left_change.table = 'left_table'
|
|
85
|
+
left_change.key = {'id' => '1'}
|
|
86
|
+
right_change = LoggedChange.new loaders[:right]
|
|
87
|
+
right_change.table = 'right_table'
|
|
88
|
+
right_change.key = {'id' => '1'}
|
|
89
|
+
|
|
90
|
+
diff = ReplicationDifference.new(loaders)
|
|
91
|
+
diff.changes[:left] = left_change
|
|
92
|
+
diff.changes[:right] = right_change
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# verify that an insert is dealt correctly with
|
|
96
|
+
left_change.type = :insert
|
|
97
|
+
right_change.type = :insert
|
|
98
|
+
|
|
99
|
+
helper.should_receive(:load_record).ordered.
|
|
100
|
+
with(:left, 'left_table', {'id' => '1'}).
|
|
101
|
+
and_return(:dummy_values)
|
|
102
|
+
helper.should_receive(:update_record).ordered.
|
|
103
|
+
with(:right, 'right_table', :dummy_values, {'id' => '1'})
|
|
104
|
+
replicator.clear_conflict :left, diff, 1
|
|
105
|
+
|
|
106
|
+
# verify that an update is dealt correctly with
|
|
107
|
+
left_change.type = :delete
|
|
108
|
+
right_change.type = :update
|
|
109
|
+
right_change.new_key = {'id' => '2'}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
helper.should_receive(:load_record).ordered.
|
|
113
|
+
with(:right, 'right_table', {'id' => '2'}).
|
|
114
|
+
and_return(:dummy_values)
|
|
115
|
+
helper.should_receive(:insert_record).ordered.
|
|
116
|
+
with(:left, 'left_table', :dummy_values)
|
|
117
|
+
replicator.clear_conflict :right, diff, 1
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# verify that a delete is dealt correctly with
|
|
121
|
+
left_change.type = :delete
|
|
122
|
+
right_change.type = :update
|
|
123
|
+
|
|
124
|
+
helper.should_receive(:delete_record).ordered.
|
|
125
|
+
with(:right, 'right_table', {'id' => '2'})
|
|
126
|
+
replicator.clear_conflict :left, diff, 1
|
|
127
|
+
ensure
|
|
128
|
+
session.left.rollback_db_transaction
|
|
129
|
+
session.right.rollback_db_transaction
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "log_replication_outcome should log conflicts correctly" do
|
|
134
|
+
session = Session.new
|
|
135
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
136
|
+
|
|
137
|
+
loaders = LoggedChangeLoaders.new(session)
|
|
138
|
+
|
|
139
|
+
diff = ReplicationDifference.new loaders
|
|
140
|
+
diff.type = :conflict
|
|
141
|
+
diff.changes[:left] = LoggedChange.new loaders[:left]
|
|
142
|
+
diff.changes[:left].table = 'scanner_records'
|
|
143
|
+
|
|
144
|
+
# should only log events if so configured
|
|
145
|
+
helper = ReplicationHelper.new(rep_run)
|
|
146
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
147
|
+
helper.should_not_receive(:log_replication_outcome)
|
|
148
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => []})
|
|
149
|
+
replicator.log_replication_outcome :ignore, diff
|
|
150
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_conflicts]})
|
|
151
|
+
replicator.log_replication_outcome :left, diff
|
|
152
|
+
|
|
153
|
+
# should log ignored conflicts correctly
|
|
154
|
+
helper = ReplicationHelper.new(rep_run)
|
|
155
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
156
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'ignored')
|
|
157
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_conflicts]})
|
|
158
|
+
replicator.log_replication_outcome :ignore, diff
|
|
159
|
+
|
|
160
|
+
# should log conflicts correctly
|
|
161
|
+
helper = ReplicationHelper.new(rep_run)
|
|
162
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
163
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'left_won')
|
|
164
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:all_conflicts]})
|
|
165
|
+
replicator.log_replication_outcome :left, diff
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "log_replication_outcome should log changes correctly" do
|
|
169
|
+
session = Session.new
|
|
170
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
171
|
+
|
|
172
|
+
loaders = LoggedChangeLoaders.new(session)
|
|
173
|
+
|
|
174
|
+
diff = ReplicationDifference.new loaders
|
|
175
|
+
diff.type = :left
|
|
176
|
+
diff.changes[:left] = LoggedChange.new loaders[:left]
|
|
177
|
+
diff.changes[:left].table = 'scanner_records'
|
|
178
|
+
|
|
179
|
+
# should only log events if so configured
|
|
180
|
+
helper = ReplicationHelper.new(rep_run)
|
|
181
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
182
|
+
helper.should_not_receive(:log_replication_outcome)
|
|
183
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => []})
|
|
184
|
+
replicator.log_replication_outcome :ignore, diff
|
|
185
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_changes]})
|
|
186
|
+
replicator.log_replication_outcome :left, diff
|
|
187
|
+
|
|
188
|
+
# should log changes correctly
|
|
189
|
+
helper = ReplicationHelper.new(rep_run)
|
|
190
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
191
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'replicated')
|
|
192
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:all_changes]})
|
|
193
|
+
replicator.log_replication_outcome :right, diff
|
|
194
|
+
|
|
195
|
+
# should log changes correctly
|
|
196
|
+
helper = ReplicationHelper.new(rep_run)
|
|
197
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
198
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'ignored')
|
|
199
|
+
helper.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_changes]})
|
|
200
|
+
replicator.log_replication_outcome :ignore, diff
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it "replicate_difference should not do anything if ignore option is given" do
|
|
204
|
+
session = Session.new
|
|
205
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
206
|
+
helper = ReplicationHelper.new(rep_run)
|
|
207
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
208
|
+
helper.stub!(:options_for_table).and_return(
|
|
209
|
+
{
|
|
210
|
+
:left_change_handling => :ignore,
|
|
211
|
+
:right_change_handling => :ignore,
|
|
212
|
+
:replication_conflict_handling => :ignore,
|
|
213
|
+
:logged_replication_events => [:ignored_changes, :ignored_conflicts]
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
loaders = LoggedChangeLoaders.new(session)
|
|
218
|
+
|
|
219
|
+
diff = ReplicationDifference.new(loaders)
|
|
220
|
+
diff.changes[:left] = LoggedChange.new loaders[:left]
|
|
221
|
+
diff.changes[:left].table = 'scanner_records'
|
|
222
|
+
|
|
223
|
+
# but logging should still happen
|
|
224
|
+
replicator.should_receive(:log_replication_outcome).
|
|
225
|
+
with(:ignore, diff).
|
|
226
|
+
exactly(3).times
|
|
227
|
+
|
|
228
|
+
helper.should_not_receive :insert_record
|
|
229
|
+
helper.should_not_receive :update_record
|
|
230
|
+
helper.should_not_receive :delete_record
|
|
231
|
+
|
|
232
|
+
diff.type = :conflict
|
|
233
|
+
replicator.replicate_difference diff
|
|
234
|
+
diff.type = :left
|
|
235
|
+
replicator.replicate_difference diff
|
|
236
|
+
diff.type = :right
|
|
237
|
+
replicator.replicate_difference diff
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "replicate_difference should call the provided Proc objects" do
|
|
241
|
+
session = Session.new
|
|
242
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
243
|
+
helper = ReplicationHelper.new(rep_run)
|
|
244
|
+
|
|
245
|
+
lambda_parameters = []
|
|
246
|
+
l = lambda do |rep_helper, diff|
|
|
247
|
+
lambda_parameters << [rep_helper, diff]
|
|
248
|
+
end
|
|
249
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
250
|
+
helper.stub!(:options_for_table).and_return(
|
|
251
|
+
{
|
|
252
|
+
:left_change_handling => l,
|
|
253
|
+
:right_change_handling => l,
|
|
254
|
+
:replication_conflict_handling => l
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
loaders = LoggedChangeLoaders.new(session)
|
|
259
|
+
|
|
260
|
+
change = LoggedChange.new loaders[:left]
|
|
261
|
+
change.table = 'scanner_records'
|
|
262
|
+
|
|
263
|
+
d1 = ReplicationDifference.new(loaders)
|
|
264
|
+
d1.type = :conflict
|
|
265
|
+
d1.changes[:left] = change
|
|
266
|
+
replicator.replicate_difference d1
|
|
267
|
+
|
|
268
|
+
d2 = ReplicationDifference.new(loaders)
|
|
269
|
+
d2.type = :left
|
|
270
|
+
d2.changes[:left] = change
|
|
271
|
+
replicator.replicate_difference d2
|
|
272
|
+
|
|
273
|
+
d3 = ReplicationDifference.new(loaders)
|
|
274
|
+
d3.type = :right
|
|
275
|
+
d3.changes[:left] = change
|
|
276
|
+
replicator.replicate_difference d3
|
|
277
|
+
|
|
278
|
+
lambda_parameters.should == [
|
|
279
|
+
[helper, d1],
|
|
280
|
+
[helper, d2],
|
|
281
|
+
[helper, d3],
|
|
282
|
+
]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it "replicate_difference should clear conflicts as per provided options" do
|
|
286
|
+
session = Session.new
|
|
287
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
288
|
+
helper = ReplicationHelper.new(rep_run)
|
|
289
|
+
|
|
290
|
+
left_change = LoggedChange.new LoggedChangeLoader.new(session, :left)
|
|
291
|
+
left_change.table = 'scanner_records'
|
|
292
|
+
right_change = LoggedChange.new LoggedChangeLoader.new(session, :right)
|
|
293
|
+
right_change.table = 'scanner_records'
|
|
294
|
+
diff = ReplicationDifference.new(session)
|
|
295
|
+
diff.type = :conflict
|
|
296
|
+
diff.changes[:left] = left_change
|
|
297
|
+
diff.changes[:right] = right_change
|
|
298
|
+
|
|
299
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
300
|
+
helper.stub!(:options_for_table).and_return({:replication_conflict_handling => :left_wins})
|
|
301
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1)
|
|
302
|
+
replicator.replicate_difference diff, 1
|
|
303
|
+
|
|
304
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
305
|
+
helper.stub!(:options_for_table).and_return({:replication_conflict_handling => :right_wins})
|
|
306
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
307
|
+
replicator.replicate_difference diff, 1
|
|
308
|
+
|
|
309
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
310
|
+
helper.stub!(:options_for_table).and_return({:replication_conflict_handling => :later_wins})
|
|
311
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1).twice
|
|
312
|
+
left_change.last_changed_at = 5.seconds.from_now
|
|
313
|
+
right_change.last_changed_at = Time.now
|
|
314
|
+
replicator.replicate_difference diff, 1
|
|
315
|
+
left_change.last_changed_at = right_change.last_changed_at = Time.now
|
|
316
|
+
replicator.replicate_difference diff, 1
|
|
317
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
318
|
+
right_change.last_changed_at = 5.seconds.from_now
|
|
319
|
+
replicator.replicate_difference diff, 1
|
|
320
|
+
|
|
321
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
322
|
+
helper.stub!(:options_for_table).and_return({:replication_conflict_handling => :earlier_wins})
|
|
323
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1).twice
|
|
324
|
+
left_change.last_changed_at = 5.seconds.ago
|
|
325
|
+
right_change.last_changed_at = Time.now
|
|
326
|
+
replicator.replicate_difference diff, 1
|
|
327
|
+
left_change.last_changed_at = right_change.last_changed_at = Time.now
|
|
328
|
+
replicator.replicate_difference diff, 1
|
|
329
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
330
|
+
right_change.last_changed_at = 5.seconds.ago
|
|
331
|
+
replicator.replicate_difference diff, 1
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it "replicate_difference should replicate :left / :right changes correctly" do
|
|
335
|
+
Initializer.configuration.include_tables 'left_table, right_table'
|
|
336
|
+
session = Session.new
|
|
337
|
+
session.left.begin_db_transaction
|
|
338
|
+
session.right.begin_db_transaction
|
|
339
|
+
begin
|
|
340
|
+
rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
|
|
341
|
+
|
|
342
|
+
left_change = LoggedChange.new LoggedChangeLoader.new(session, :left)
|
|
343
|
+
left_change.table = 'left_table'
|
|
344
|
+
left_change.key = {'id' => '1'}
|
|
345
|
+
right_change = LoggedChange.new LoggedChangeLoader.new(session, :right)
|
|
346
|
+
right_change.table = 'right_table'
|
|
347
|
+
right_change.key = {'id' => '1'}
|
|
348
|
+
|
|
349
|
+
diff = ReplicationDifference.new(session)
|
|
350
|
+
|
|
351
|
+
# verify insert behaviour
|
|
352
|
+
left_change.type = :insert
|
|
353
|
+
diff.type = :left
|
|
354
|
+
diff.changes[:left] = left_change
|
|
355
|
+
diff.changes[:right] = nil
|
|
356
|
+
|
|
357
|
+
helper = ReplicationHelper.new(rep_run)
|
|
358
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
359
|
+
replicator.should_receive(:log_replication_outcome).with(:left, diff)
|
|
360
|
+
helper.should_receive(:load_record).with(:left, 'left_table', {'id' => '1'}).
|
|
361
|
+
and_return(:dummy_values)
|
|
362
|
+
helper.should_receive(:insert_record).with(:right, 'right_table', :dummy_values)
|
|
363
|
+
replicator.replicate_difference diff
|
|
364
|
+
|
|
365
|
+
# verify update behaviour
|
|
366
|
+
right_change.type = :update
|
|
367
|
+
right_change.new_key = {'id' => '2'}
|
|
368
|
+
diff.type = :right
|
|
369
|
+
diff.changes[:right] = right_change
|
|
370
|
+
|
|
371
|
+
helper = ReplicationHelper.new(rep_run)
|
|
372
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
373
|
+
replicator.should_receive(:log_replication_outcome).with(:right, diff)
|
|
374
|
+
helper.should_receive(:load_record).with(:right, 'right_table', {'id' => '2'}).
|
|
375
|
+
and_return(:dummy_values)
|
|
376
|
+
helper.should_receive(:update_record).with(:left, 'left_table', :dummy_values, {'id' => '1'})
|
|
377
|
+
replicator.replicate_difference diff
|
|
378
|
+
|
|
379
|
+
# verify delete behaviour
|
|
380
|
+
right_change.type = :delete
|
|
381
|
+
|
|
382
|
+
helper = ReplicationHelper.new(rep_run)
|
|
383
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
384
|
+
replicator.should_receive(:log_replication_outcome).with(:right, diff)
|
|
385
|
+
helper.should_receive(:delete_record).with(:left, 'left_table', {'id' => '1'})
|
|
386
|
+
replicator.replicate_difference diff
|
|
387
|
+
ensure
|
|
388
|
+
session.left.rollback_db_transaction
|
|
389
|
+
session.right.rollback_db_transaction
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
it "replicate_difference should handle inserts failing due duplicate records getting created after the original diff was loaded" do
|
|
394
|
+
begin
|
|
395
|
+
config = deep_copy(standard_config)
|
|
396
|
+
config.options[:committer] = :never_commit
|
|
397
|
+
config.options[:replication_conflict_handling] = :right_wins
|
|
398
|
+
|
|
399
|
+
session = Session.new(config)
|
|
400
|
+
|
|
401
|
+
session.left.insert_record 'extender_no_record', {
|
|
402
|
+
'id' => '1',
|
|
403
|
+
'name' => 'bla'
|
|
404
|
+
}
|
|
405
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
406
|
+
'change_table' => 'extender_no_record',
|
|
407
|
+
'change_key' => 'id|1',
|
|
408
|
+
'change_type' => 'I',
|
|
409
|
+
'change_time' => Time.now
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
414
|
+
helper = ReplicationHelper.new(rep_run)
|
|
415
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
416
|
+
|
|
417
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
418
|
+
diff.load
|
|
419
|
+
|
|
420
|
+
session.right.insert_record 'extender_no_record', {
|
|
421
|
+
'id' => '1',
|
|
422
|
+
'name' => 'blub'
|
|
423
|
+
}
|
|
424
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
425
|
+
'change_table' => 'extender_no_record',
|
|
426
|
+
'change_key' => 'id|1',
|
|
427
|
+
'change_type' => 'I',
|
|
428
|
+
'change_time' => Time.now
|
|
429
|
+
}
|
|
430
|
+
replicator.replicate_difference diff, 2
|
|
431
|
+
|
|
432
|
+
session.left.select_record(:table => "extender_no_record").should == {
|
|
433
|
+
'id' => 1,
|
|
434
|
+
'name' => 'blub'
|
|
435
|
+
}
|
|
436
|
+
ensure
|
|
437
|
+
Committers::NeverCommitter.rollback_current_session
|
|
438
|
+
if session
|
|
439
|
+
session.left.execute "delete from extender_no_record"
|
|
440
|
+
session.right.execute "delete from extender_no_record"
|
|
441
|
+
session.left.execute "delete from rr_pending_changes"
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "replicate_difference should handle inserts failing due the new record being deleted after the original diff was loaded" do
|
|
447
|
+
begin
|
|
448
|
+
config = deep_copy(standard_config)
|
|
449
|
+
config.options[:committer] = :never_commit
|
|
450
|
+
|
|
451
|
+
session = Session.new(config)
|
|
452
|
+
|
|
453
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
454
|
+
'change_table' => 'extender_no_record',
|
|
455
|
+
'change_key' => 'id|1',
|
|
456
|
+
'change_type' => 'I',
|
|
457
|
+
'change_time' => Time.now
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
461
|
+
helper = ReplicationHelper.new(rep_run)
|
|
462
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
463
|
+
|
|
464
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
465
|
+
diff.load
|
|
466
|
+
|
|
467
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
468
|
+
'change_table' => 'extender_no_record',
|
|
469
|
+
'change_key' => 'id|1',
|
|
470
|
+
'change_type' => 'D',
|
|
471
|
+
'change_time' => Time.now
|
|
472
|
+
}
|
|
473
|
+
replicator.replicate_difference diff, 2
|
|
474
|
+
|
|
475
|
+
# no rspec expectation: success is when we get till here without exception
|
|
476
|
+
ensure
|
|
477
|
+
Committers::NeverCommitter.rollback_current_session
|
|
478
|
+
session.left.execute "delete from rr_pending_changes" if session
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
it "replicate_difference should raise Exception if all replication attempts have been exceeded" do
|
|
483
|
+
rep_run = ReplicationRun.new Session.new, TaskSweeper.new(1)
|
|
484
|
+
helper = ReplicationHelper.new(rep_run)
|
|
485
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
486
|
+
lambda {replicator.replicate_difference :dummy_diff, 0}.
|
|
487
|
+
should raise_error(Exception, "max replication attempts exceeded")
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
it "replicate_difference should handle updates rejected by the database" do
|
|
491
|
+
begin
|
|
492
|
+
config = deep_copy(standard_config)
|
|
493
|
+
config.options[:committer] = :never_commit
|
|
494
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
495
|
+
|
|
496
|
+
session = Session.new(config)
|
|
497
|
+
session.left.execute "delete from rr_logged_events"
|
|
498
|
+
|
|
499
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
500
|
+
'change_table' => 'scanner_records',
|
|
501
|
+
'change_key' => 'id|1',
|
|
502
|
+
'change_new_key' => 'id|2',
|
|
503
|
+
'change_type' => 'U',
|
|
504
|
+
'change_time' => Time.now
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
508
|
+
helper = ReplicationHelper.new(rep_run)
|
|
509
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
510
|
+
|
|
511
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
512
|
+
diff.load
|
|
513
|
+
|
|
514
|
+
lambda {replicator.replicate_difference diff, 1}.should raise_error(/duplicate/i)
|
|
515
|
+
|
|
516
|
+
# Verify that the transaction has not become invalid
|
|
517
|
+
helper.log_replication_outcome diff, "bla", "blub"
|
|
518
|
+
|
|
519
|
+
row = session.left.select_one("select * from rr_logged_events")
|
|
520
|
+
row['change_table'].should == 'scanner_records'
|
|
521
|
+
row['change_key'].should == '1'
|
|
522
|
+
row['description'].should == 'bla'
|
|
523
|
+
|
|
524
|
+
ensure
|
|
525
|
+
Committers::NeverCommitter.rollback_current_session
|
|
526
|
+
if session
|
|
527
|
+
session.left.execute "delete from rr_pending_changes"
|
|
528
|
+
session.left.execute "delete from rr_logged_events"
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
it "replicate_difference should handle deletes rejected by the database" do
|
|
534
|
+
begin
|
|
535
|
+
config = deep_copy(standard_config)
|
|
536
|
+
config.options[:committer] = :never_commit
|
|
537
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
538
|
+
|
|
539
|
+
session = Session.new(config)
|
|
540
|
+
|
|
541
|
+
session.left.select_all("select * from rr_logged_events").should == []
|
|
542
|
+
|
|
543
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
544
|
+
'change_table' => 'referenced_table',
|
|
545
|
+
'change_key' => 'first_id|1|second_id|2',
|
|
546
|
+
'change_new_key' => nil,
|
|
547
|
+
'change_type' => 'D',
|
|
548
|
+
'change_time' => Time.now
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
552
|
+
helper = ReplicationHelper.new(rep_run)
|
|
553
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
554
|
+
|
|
555
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
556
|
+
diff.load
|
|
557
|
+
|
|
558
|
+
lambda {replicator.replicate_difference diff, 1}.should raise_error(/referencing_table_fkey/)
|
|
559
|
+
|
|
560
|
+
# Verify that the transaction has not become invalid
|
|
561
|
+
helper.log_replication_outcome diff, "bla", "blub"
|
|
562
|
+
|
|
563
|
+
row = session.left.select_one("select * from rr_logged_events")
|
|
564
|
+
row['change_table'].should == 'referenced_table'
|
|
565
|
+
row['change_key'].should =~ /first_id.*1.*second_id.*2/
|
|
566
|
+
row['description'].should == 'bla'
|
|
567
|
+
|
|
568
|
+
ensure
|
|
569
|
+
Committers::NeverCommitter.rollback_current_session
|
|
570
|
+
if session
|
|
571
|
+
session.left.execute "delete from rr_pending_changes"
|
|
572
|
+
session.left.execute "delete from rr_logged_events"
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
it "replicate_difference should handle deletes failing due to the target record vanishing" do
|
|
578
|
+
begin
|
|
579
|
+
config = deep_copy(standard_config)
|
|
580
|
+
config.options[:committer] = :never_commit
|
|
581
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
582
|
+
|
|
583
|
+
session = Session.new(config)
|
|
584
|
+
|
|
585
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
586
|
+
'change_table' => 'scanner_records',
|
|
587
|
+
'change_key' => 'id|3',
|
|
588
|
+
'change_new_key' => nil,
|
|
589
|
+
'change_type' => 'D',
|
|
590
|
+
'change_time' => Time.now
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
594
|
+
helper = ReplicationHelper.new(rep_run)
|
|
595
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
596
|
+
|
|
597
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
598
|
+
diff.load
|
|
599
|
+
|
|
600
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
601
|
+
'change_table' => 'scanner_records',
|
|
602
|
+
'change_key' => 'id|3',
|
|
603
|
+
'change_new_key' => 'id|4',
|
|
604
|
+
'change_type' => 'U',
|
|
605
|
+
'change_time' => Time.now
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
replicator.replicate_difference diff, 2
|
|
609
|
+
|
|
610
|
+
session.right.select_one("select * from scanner_records where id = 4").
|
|
611
|
+
should be_nil
|
|
612
|
+
ensure
|
|
613
|
+
Committers::NeverCommitter.rollback_current_session
|
|
614
|
+
if session
|
|
615
|
+
session.left.execute "delete from rr_pending_changes"
|
|
616
|
+
session.left.execute "delete from rr_logged_events"
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
it "replicate_difference should handle updates failing due to the source record being deleted after the original diff was loaded" do
|
|
622
|
+
begin
|
|
623
|
+
config = deep_copy(standard_config)
|
|
624
|
+
config.options[:committer] = :never_commit
|
|
625
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
626
|
+
|
|
627
|
+
session = Session.new(config)
|
|
628
|
+
|
|
629
|
+
session.left.insert_record 'extender_no_record', {
|
|
630
|
+
'id' => '2',
|
|
631
|
+
'name' => 'bla'
|
|
632
|
+
}
|
|
633
|
+
session.right.insert_record 'extender_no_record', {
|
|
634
|
+
'id' => '2',
|
|
635
|
+
'name' => 'blub'
|
|
636
|
+
}
|
|
637
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
638
|
+
'change_table' => 'extender_no_record',
|
|
639
|
+
'change_key' => 'id|1',
|
|
640
|
+
'change_new_key' => 'id|2',
|
|
641
|
+
'change_type' => 'U',
|
|
642
|
+
'change_time' => Time.now
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
646
|
+
helper = ReplicationHelper.new(rep_run)
|
|
647
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
648
|
+
|
|
649
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
650
|
+
diff.load
|
|
651
|
+
|
|
652
|
+
session.left.delete_record 'extender_no_record', {'id' => '2'}
|
|
653
|
+
|
|
654
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
655
|
+
'change_table' => 'extender_no_record',
|
|
656
|
+
'change_key' => 'id|2',
|
|
657
|
+
'change_type' => 'D',
|
|
658
|
+
'change_time' => Time.now
|
|
659
|
+
}
|
|
660
|
+
replicator.replicate_difference diff, 2
|
|
661
|
+
|
|
662
|
+
session.right.select_one("select * from extender_no_record").should be_nil
|
|
663
|
+
ensure
|
|
664
|
+
Committers::NeverCommitter.rollback_current_session
|
|
665
|
+
if session
|
|
666
|
+
session.left.execute "delete from extender_no_record"
|
|
667
|
+
session.right.execute "delete from extender_no_record"
|
|
668
|
+
session.left.execute "delete from rr_pending_changes"
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
it "replicate_difference should handle updates failing due to the target record being deleted after the original diff was loaded" do
|
|
674
|
+
begin
|
|
675
|
+
config = deep_copy(standard_config)
|
|
676
|
+
config.options[:committer] = :never_commit
|
|
677
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
678
|
+
|
|
679
|
+
session = Session.new(config)
|
|
680
|
+
|
|
681
|
+
session.left.insert_record 'extender_no_record', {
|
|
682
|
+
'id' => '2',
|
|
683
|
+
'name' => 'bla'
|
|
684
|
+
}
|
|
685
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
686
|
+
'change_table' => 'extender_no_record',
|
|
687
|
+
'change_key' => 'id|1',
|
|
688
|
+
'change_new_key' => 'id|2',
|
|
689
|
+
'change_type' => 'U',
|
|
690
|
+
'change_time' => Time.now
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
rep_run = ReplicationRun.new session, TaskSweeper.new(1)
|
|
694
|
+
helper = ReplicationHelper.new(rep_run)
|
|
695
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
696
|
+
|
|
697
|
+
diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
|
|
698
|
+
diff.load
|
|
699
|
+
|
|
700
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
701
|
+
'change_table' => 'extender_no_record',
|
|
702
|
+
'change_key' => 'id|1',
|
|
703
|
+
'change_type' => 'D',
|
|
704
|
+
'change_time' => Time.now
|
|
705
|
+
}
|
|
706
|
+
replicator.replicate_difference diff, 2
|
|
707
|
+
|
|
708
|
+
session.right.select_record(:table => "extender_no_record").should == {
|
|
709
|
+
'id' => 2,
|
|
710
|
+
'name' => 'bla'
|
|
711
|
+
}
|
|
712
|
+
ensure
|
|
713
|
+
Committers::NeverCommitter.rollback_current_session
|
|
714
|
+
if session
|
|
715
|
+
session.left.execute "delete from extender_no_record"
|
|
716
|
+
session.right.execute "delete from extender_no_record"
|
|
717
|
+
session.left.execute "delete from rr_pending_changes"
|
|
718
|
+
end
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
end
|