rubyrep 1.0.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.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +137 -0
- data/README.txt +37 -0
- data/Rakefile +30 -0
- data/bin/rubyrep +8 -0
- data/config/hoe.rb +72 -0
- data/config/mysql_config.rb +25 -0
- data/config/postgres_config.rb +21 -0
- data/config/proxied_test_config.rb +14 -0
- data/config/redmine_config.rb +17 -0
- data/config/rep_config.rb +20 -0
- data/config/requirements.rb +32 -0
- data/config/test_config.rb +20 -0
- data/lib/rubyrep/base_runner.rb +195 -0
- data/lib/rubyrep/command_runner.rb +144 -0
- data/lib/rubyrep/committers/buffered_committer.rb +140 -0
- data/lib/rubyrep/committers/committers.rb +146 -0
- data/lib/rubyrep/configuration.rb +240 -0
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +133 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +284 -0
- data/lib/rubyrep/connection_extenders/mysql_extender.rb +168 -0
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +261 -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/logged_change.rb +326 -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 +318 -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 +91 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +204 -0
- data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
- data/lib/rubyrep/replication_helper.rb +104 -0
- data/lib/rubyrep/replication_initializer.rb +307 -0
- data/lib/rubyrep/replication_run.rb +48 -0
- data/lib/rubyrep/replication_runner.rb +138 -0
- data/lib/rubyrep/replicators/replicators.rb +37 -0
- data/lib/rubyrep/replicators/two_way_replicator.rb +334 -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 +177 -0
- data/lib/rubyrep/sync_helper.rb +111 -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 +38 -0
- data/lib/rubyrep/table_sorter.rb +70 -0
- data/lib/rubyrep/table_spec_resolver.rb +136 -0
- data/lib/rubyrep/table_sync.rb +68 -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 +92 -0
- data/lib/rubyrep/version.rb +9 -0
- data/lib/rubyrep.rb +68 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/sims/performance/big_rep_spec.rb +100 -0
- data/sims/performance/big_scan_spec.rb +57 -0
- data/sims/performance/big_sync_spec.rb +141 -0
- data/sims/performance/performance.rake +228 -0
- data/sims/sim_helper.rb +24 -0
- data/spec/base_runner_spec.rb +218 -0
- data/spec/buffered_committer_spec.rb +271 -0
- data/spec/command_runner_spec.rb +145 -0
- data/spec/committers_spec.rb +174 -0
- data/spec/configuration_spec.rb +198 -0
- data/spec/connection_extender_interface_spec.rb +138 -0
- data/spec/connection_extenders_registration_spec.rb +129 -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/generate_runner_spec.rb +84 -0
- data/spec/initializer_spec.rb +46 -0
- data/spec/logged_change_spec.rb +480 -0
- data/spec/postgresql_replication_spec.rb +48 -0
- data/spec/postgresql_support_spec.rb +57 -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 +399 -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 +160 -0
- data/spec/replication_extender_interface_spec.rb +365 -0
- data/spec/replication_extenders_spec.rb +32 -0
- data/spec/replication_helper_spec.rb +121 -0
- data/spec/replication_initializer_spec.rb +477 -0
- data/spec/replication_run_spec.rb +166 -0
- data/spec/replication_runner_spec.rb +213 -0
- data/spec/replicators_spec.rb +31 -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 +212 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +295 -0
- data/spec/sync_helper_spec.rb +157 -0
- data/spec/sync_runner_spec.rb +78 -0
- data/spec/syncers_spec.rb +171 -0
- data/spec/table_scan_helper_spec.rb +29 -0
- data/spec/table_scan_spec.rb +49 -0
- data/spec/table_sorter_spec.rb +31 -0
- data/spec/table_spec_resolver_spec.rb +102 -0
- data/spec/table_sync_spec.rb +84 -0
- data/spec/trigger_mode_switcher_spec.rb +83 -0
- data/spec/two_way_replicator_spec.rb +551 -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 +86 -0
- data/tasks/database.rake +439 -0
- data/tasks/deployment.rake +29 -0
- data/tasks/environment.rake +9 -0
- data/tasks/java.rake +37 -0
- data/tasks/redmine_test.rake +47 -0
- data/tasks/rspec.rake +68 -0
- data/tasks/rubyrep.tailor +18 -0
- data/tasks/stats.rake +19 -0
- data/tasks/task_helper.rb +20 -0
- data.tar.gz.sig +0 -0
- metadata +243 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,551 @@
|
|
|
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)
|
|
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)
|
|
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)
|
|
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 "options_for_table should return the correct options for the table" do
|
|
72
|
+
Initializer.configuration.options = {:a => 1, :b => 2}
|
|
73
|
+
Initializer.configuration.add_table_options 'scanner_records', {:b => 3}
|
|
74
|
+
rep_run = ReplicationRun.new(Session.new)
|
|
75
|
+
helper = ReplicationHelper.new(rep_run)
|
|
76
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
77
|
+
options = replicator.options_for_table('scanner_records')
|
|
78
|
+
options[:a].should == 1
|
|
79
|
+
options[:b].should == 3
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "options_for_table should merge the configured options into the default two way replicator options" do
|
|
83
|
+
rep_run = ReplicationRun.new(Session.new)
|
|
84
|
+
helper = ReplicationHelper.new(rep_run)
|
|
85
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
86
|
+
replicator.options_for_table('scanner_records').include?(:left_change_handling).should be_true
|
|
87
|
+
replicator.options_for_table('scanner_records').include?(:right_change_handling).should be_true
|
|
88
|
+
replicator.options_for_table('scanner_records').include?(:replication_conflict_handling).should be_true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "clear_conflicts should update the correct database with the correct action" do
|
|
92
|
+
Initializer.configuration.include_tables 'left_table, right_table'
|
|
93
|
+
session = Session.new
|
|
94
|
+
session.left.begin_db_transaction
|
|
95
|
+
session.right.begin_db_transaction
|
|
96
|
+
begin
|
|
97
|
+
rep_run = ReplicationRun.new(session)
|
|
98
|
+
helper = ReplicationHelper.new(rep_run)
|
|
99
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
100
|
+
|
|
101
|
+
left_change = LoggedChange.new session, :left
|
|
102
|
+
left_change.table = 'left_table'
|
|
103
|
+
left_change.key = {'id' => '1'}
|
|
104
|
+
right_change = LoggedChange.new session, :right
|
|
105
|
+
right_change.table = 'right_table'
|
|
106
|
+
right_change.key = {'id' => '1'}
|
|
107
|
+
|
|
108
|
+
diff = ReplicationDifference.new(session)
|
|
109
|
+
diff.changes[:left] = left_change
|
|
110
|
+
diff.changes[:right] = right_change
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# verify that an insert is dealt correctly with
|
|
114
|
+
left_change.type = :insert
|
|
115
|
+
right_change.type = :insert
|
|
116
|
+
|
|
117
|
+
helper.should_receive(:load_record).ordered.
|
|
118
|
+
with(:left, 'left_table', {'id' => '1'}).
|
|
119
|
+
and_return(:dummy_values)
|
|
120
|
+
helper.should_receive(:update_record).ordered.
|
|
121
|
+
with(:right, 'right_table', :dummy_values, {'id' => '1'})
|
|
122
|
+
replicator.clear_conflict :left, diff, 1
|
|
123
|
+
|
|
124
|
+
# verify that an update is dealt correctly with
|
|
125
|
+
left_change.type = :delete
|
|
126
|
+
right_change.type = :update
|
|
127
|
+
right_change.new_key = {'id' => '2'}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
helper.should_receive(:load_record).ordered.
|
|
131
|
+
with(:right, 'right_table', {'id' => '2'}).
|
|
132
|
+
and_return(:dummy_values)
|
|
133
|
+
helper.should_receive(:insert_record).ordered.
|
|
134
|
+
with(:left, 'left_table', :dummy_values)
|
|
135
|
+
replicator.clear_conflict :right, diff, 1
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# verify that a delete is dealt correctly with
|
|
139
|
+
left_change.type = :delete
|
|
140
|
+
right_change.type = :update
|
|
141
|
+
|
|
142
|
+
helper.should_receive(:delete_record).ordered.
|
|
143
|
+
with(:right, 'right_table', {'id' => '2'})
|
|
144
|
+
replicator.clear_conflict :left, diff, 1
|
|
145
|
+
ensure
|
|
146
|
+
session.left.rollback_db_transaction
|
|
147
|
+
session.right.rollback_db_transaction
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "log_replication_outcome should log conflicts correctly" do
|
|
152
|
+
session = Session.new
|
|
153
|
+
rep_run = ReplicationRun.new(session)
|
|
154
|
+
|
|
155
|
+
diff = ReplicationDifference.new session
|
|
156
|
+
diff.type = :conflict
|
|
157
|
+
diff.changes[:left] = LoggedChange.new session, :left
|
|
158
|
+
diff.changes[:left].table = 'scanner_records'
|
|
159
|
+
|
|
160
|
+
# should only log events if so configured
|
|
161
|
+
helper = ReplicationHelper.new(rep_run)
|
|
162
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
163
|
+
helper.should_not_receive(:log_replication_outcome)
|
|
164
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => []})
|
|
165
|
+
replicator.log_replication_outcome :ignore, diff
|
|
166
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_conflicts]})
|
|
167
|
+
replicator.log_replication_outcome :left, diff
|
|
168
|
+
|
|
169
|
+
# should log ignored conflicts correctly
|
|
170
|
+
helper = ReplicationHelper.new(rep_run)
|
|
171
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
172
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'ignored')
|
|
173
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_conflicts]})
|
|
174
|
+
replicator.log_replication_outcome :ignore, diff
|
|
175
|
+
|
|
176
|
+
# should log conflicts correctly
|
|
177
|
+
helper = ReplicationHelper.new(rep_run)
|
|
178
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
179
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'left_won')
|
|
180
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:all_conflicts]})
|
|
181
|
+
replicator.log_replication_outcome :left, diff
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "log_replication_outcome should log changes correctly" do
|
|
185
|
+
session = Session.new
|
|
186
|
+
rep_run = ReplicationRun.new(session)
|
|
187
|
+
|
|
188
|
+
diff = ReplicationDifference.new session
|
|
189
|
+
diff.type = :left
|
|
190
|
+
diff.changes[:left] = LoggedChange.new session, :left
|
|
191
|
+
diff.changes[:left].table = 'scanner_records'
|
|
192
|
+
|
|
193
|
+
# should only log events if so configured
|
|
194
|
+
helper = ReplicationHelper.new(rep_run)
|
|
195
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
196
|
+
helper.should_not_receive(:log_replication_outcome)
|
|
197
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => []})
|
|
198
|
+
replicator.log_replication_outcome :ignore, diff
|
|
199
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_changes]})
|
|
200
|
+
replicator.log_replication_outcome :left, diff
|
|
201
|
+
|
|
202
|
+
# should log changes correctly
|
|
203
|
+
helper = ReplicationHelper.new(rep_run)
|
|
204
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
205
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'replicated')
|
|
206
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:all_changes]})
|
|
207
|
+
replicator.log_replication_outcome :right, diff
|
|
208
|
+
|
|
209
|
+
# should log changes correctly
|
|
210
|
+
helper = ReplicationHelper.new(rep_run)
|
|
211
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
212
|
+
helper.should_receive(:log_replication_outcome).with(diff, 'ignored')
|
|
213
|
+
replicator.stub!(:options_for_table).and_return({:logged_replication_events => [:ignored_changes]})
|
|
214
|
+
replicator.log_replication_outcome :ignore, diff
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it "replicate_difference should not do anything if ignore option is given" do
|
|
218
|
+
session = Session.new
|
|
219
|
+
rep_run = ReplicationRun.new(session)
|
|
220
|
+
helper = ReplicationHelper.new(rep_run)
|
|
221
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
222
|
+
replicator.stub!(:options_for_table).and_return(
|
|
223
|
+
{
|
|
224
|
+
:left_change_handling => :ignore,
|
|
225
|
+
:right_change_handling => :ignore,
|
|
226
|
+
:replication_conflict_handling => :ignore,
|
|
227
|
+
:logged_replication_events => [:ignored_changes, :ignored_conflicts]
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
diff = ReplicationDifference.new(session)
|
|
232
|
+
diff.changes[:left] = LoggedChange.new session, :left
|
|
233
|
+
diff.changes[:left].table = 'scanner_records'
|
|
234
|
+
|
|
235
|
+
# but logging should still happen
|
|
236
|
+
replicator.should_receive(:log_replication_outcome).
|
|
237
|
+
with(:ignore, diff).
|
|
238
|
+
exactly(3).times
|
|
239
|
+
|
|
240
|
+
helper.should_not_receive :insert_record
|
|
241
|
+
helper.should_not_receive :update_record
|
|
242
|
+
helper.should_not_receive :delete_record
|
|
243
|
+
|
|
244
|
+
diff.type = :conflict
|
|
245
|
+
replicator.replicate_difference diff
|
|
246
|
+
diff.type = :left
|
|
247
|
+
replicator.replicate_difference diff
|
|
248
|
+
diff.type = :right
|
|
249
|
+
replicator.replicate_difference diff
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "replicate_difference should call the provided Proc objects" do
|
|
253
|
+
session = Session.new
|
|
254
|
+
rep_run = ReplicationRun.new(session)
|
|
255
|
+
helper = ReplicationHelper.new(rep_run)
|
|
256
|
+
|
|
257
|
+
lambda_parameters = []
|
|
258
|
+
l = lambda do |rep_helper, diff|
|
|
259
|
+
lambda_parameters << [rep_helper, diff]
|
|
260
|
+
end
|
|
261
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
262
|
+
replicator.stub!(:options_for_table).and_return(
|
|
263
|
+
{
|
|
264
|
+
:left_change_handling => l,
|
|
265
|
+
:right_change_handling => l,
|
|
266
|
+
:replication_conflict_handling => l
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
change = LoggedChange.new session, :left
|
|
271
|
+
change.table = 'scanner_records'
|
|
272
|
+
|
|
273
|
+
d1 = ReplicationDifference.new(session)
|
|
274
|
+
d1.type = :conflict
|
|
275
|
+
d1.changes[:left] = change
|
|
276
|
+
replicator.replicate_difference d1
|
|
277
|
+
|
|
278
|
+
d2 = ReplicationDifference.new(session)
|
|
279
|
+
d2.type = :left
|
|
280
|
+
d2.changes[:left] = change
|
|
281
|
+
replicator.replicate_difference d2
|
|
282
|
+
|
|
283
|
+
d3 = ReplicationDifference.new(session)
|
|
284
|
+
d3.type = :right
|
|
285
|
+
d3.changes[:left] = change
|
|
286
|
+
replicator.replicate_difference d3
|
|
287
|
+
|
|
288
|
+
lambda_parameters.should == [
|
|
289
|
+
[helper, d1],
|
|
290
|
+
[helper, d2],
|
|
291
|
+
[helper, d3],
|
|
292
|
+
]
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "replicate_difference should clear conflicts as per provided options" do
|
|
296
|
+
session = Session.new
|
|
297
|
+
rep_run = ReplicationRun.new(session)
|
|
298
|
+
helper = ReplicationHelper.new(rep_run)
|
|
299
|
+
|
|
300
|
+
left_change = LoggedChange.new session, :left
|
|
301
|
+
left_change.table = 'scanner_records'
|
|
302
|
+
right_change = LoggedChange.new session, :right
|
|
303
|
+
right_change.table = 'scanner_records'
|
|
304
|
+
diff = ReplicationDifference.new(session)
|
|
305
|
+
diff.type = :conflict
|
|
306
|
+
diff.changes[:left] = left_change
|
|
307
|
+
diff.changes[:right] = right_change
|
|
308
|
+
|
|
309
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
310
|
+
replicator.stub!(:options_for_table).and_return({:replication_conflict_handling => :left_wins})
|
|
311
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1)
|
|
312
|
+
replicator.replicate_difference diff, 1
|
|
313
|
+
|
|
314
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
315
|
+
replicator.stub!(:options_for_table).and_return({:replication_conflict_handling => :right_wins})
|
|
316
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
317
|
+
replicator.replicate_difference diff, 1
|
|
318
|
+
|
|
319
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
320
|
+
replicator.stub!(:options_for_table).and_return({:replication_conflict_handling => :later_wins})
|
|
321
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1).twice
|
|
322
|
+
left_change.last_changed_at = 5.seconds.from_now
|
|
323
|
+
right_change.last_changed_at = Time.now
|
|
324
|
+
replicator.replicate_difference diff, 1
|
|
325
|
+
left_change.last_changed_at = right_change.last_changed_at = Time.now
|
|
326
|
+
replicator.replicate_difference diff, 1
|
|
327
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
328
|
+
right_change.last_changed_at = 5.seconds.from_now
|
|
329
|
+
replicator.replicate_difference diff, 1
|
|
330
|
+
|
|
331
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
332
|
+
replicator.stub!(:options_for_table).and_return({:replication_conflict_handling => :earlier_wins})
|
|
333
|
+
replicator.should_receive(:clear_conflict).with(:left, diff, 1).twice
|
|
334
|
+
left_change.last_changed_at = 5.seconds.ago
|
|
335
|
+
right_change.last_changed_at = Time.now
|
|
336
|
+
replicator.replicate_difference diff, 1
|
|
337
|
+
left_change.last_changed_at = right_change.last_changed_at = Time.now
|
|
338
|
+
replicator.replicate_difference diff, 1
|
|
339
|
+
replicator.should_receive(:clear_conflict).with(:right, diff, 1)
|
|
340
|
+
right_change.last_changed_at = 5.seconds.ago
|
|
341
|
+
replicator.replicate_difference diff, 1
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "replicate_difference should replicate :left / :right changes correctly" do
|
|
345
|
+
Initializer.configuration.include_tables 'left_table, right_table'
|
|
346
|
+
session = Session.new
|
|
347
|
+
session.left.begin_db_transaction
|
|
348
|
+
session.right.begin_db_transaction
|
|
349
|
+
begin
|
|
350
|
+
rep_run = ReplicationRun.new(session)
|
|
351
|
+
|
|
352
|
+
left_change = LoggedChange.new session, :left
|
|
353
|
+
left_change.table = 'left_table'
|
|
354
|
+
left_change.key = {'id' => '1'}
|
|
355
|
+
right_change = LoggedChange.new session, :right
|
|
356
|
+
right_change.table = 'right_table'
|
|
357
|
+
right_change.key = {'id' => '1'}
|
|
358
|
+
|
|
359
|
+
diff = ReplicationDifference.new(session)
|
|
360
|
+
|
|
361
|
+
# verify insert behaviour
|
|
362
|
+
left_change.type = :insert
|
|
363
|
+
diff.type = :left
|
|
364
|
+
diff.changes[:left] = left_change
|
|
365
|
+
diff.changes[:right] = nil
|
|
366
|
+
|
|
367
|
+
helper = ReplicationHelper.new(rep_run)
|
|
368
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
369
|
+
replicator.should_receive(:log_replication_outcome).with(:left, diff)
|
|
370
|
+
helper.should_receive(:load_record).with(:left, 'left_table', {'id' => '1'}).
|
|
371
|
+
and_return(:dummy_values)
|
|
372
|
+
helper.should_receive(:insert_record).with(:right, 'right_table', :dummy_values)
|
|
373
|
+
replicator.replicate_difference diff
|
|
374
|
+
|
|
375
|
+
# verify update behaviour
|
|
376
|
+
right_change.type = :update
|
|
377
|
+
right_change.new_key = {'id' => '2'}
|
|
378
|
+
diff.type = :right
|
|
379
|
+
diff.changes[:right] = right_change
|
|
380
|
+
|
|
381
|
+
helper = ReplicationHelper.new(rep_run)
|
|
382
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
383
|
+
replicator.should_receive(:log_replication_outcome).with(:right, diff)
|
|
384
|
+
helper.should_receive(:load_record).with(:right, 'right_table', {'id' => '2'}).
|
|
385
|
+
and_return(:dummy_values)
|
|
386
|
+
helper.should_receive(:update_record).with(:left, 'left_table', :dummy_values, {'id' => '1'})
|
|
387
|
+
replicator.replicate_difference diff
|
|
388
|
+
|
|
389
|
+
# verify delete behaviour
|
|
390
|
+
right_change.type = :delete
|
|
391
|
+
|
|
392
|
+
helper = ReplicationHelper.new(rep_run)
|
|
393
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
394
|
+
replicator.should_receive(:log_replication_outcome).with(:right, diff)
|
|
395
|
+
helper.should_receive(:delete_record).with(:left, 'left_table', {'id' => '1'})
|
|
396
|
+
replicator.replicate_difference diff
|
|
397
|
+
ensure
|
|
398
|
+
session.left.rollback_db_transaction
|
|
399
|
+
session.right.rollback_db_transaction
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "replicate_difference should handle inserts failing due duplicate records getting created after the original diff was loaded" do
|
|
404
|
+
begin
|
|
405
|
+
config = deep_copy(standard_config)
|
|
406
|
+
config.options[:committer] = :never_commit
|
|
407
|
+
config.options[:replication_conflict_handling] = :right_wins
|
|
408
|
+
|
|
409
|
+
session = Session.new(config)
|
|
410
|
+
|
|
411
|
+
session.left.insert_record 'extender_no_record', {
|
|
412
|
+
'id' => '1',
|
|
413
|
+
'name' => 'bla'
|
|
414
|
+
}
|
|
415
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
416
|
+
'change_table' => 'extender_no_record',
|
|
417
|
+
'change_key' => 'id|1',
|
|
418
|
+
'change_type' => 'I',
|
|
419
|
+
'change_time' => Time.now
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
rep_run = ReplicationRun.new session
|
|
424
|
+
helper = ReplicationHelper.new(rep_run)
|
|
425
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
426
|
+
|
|
427
|
+
diff = ReplicationDifference.new session
|
|
428
|
+
diff.load
|
|
429
|
+
|
|
430
|
+
session.right.insert_record 'extender_no_record', {
|
|
431
|
+
'id' => '1',
|
|
432
|
+
'name' => 'blub'
|
|
433
|
+
}
|
|
434
|
+
session.right.insert_record 'rr_pending_changes', {
|
|
435
|
+
'change_table' => 'extender_no_record',
|
|
436
|
+
'change_key' => 'id|1',
|
|
437
|
+
'change_type' => 'I',
|
|
438
|
+
'change_time' => Time.now
|
|
439
|
+
}
|
|
440
|
+
replicator.replicate_difference diff, 2
|
|
441
|
+
|
|
442
|
+
session.left.select_one("select * from extender_no_record").should == {
|
|
443
|
+
'id' => '1',
|
|
444
|
+
'name' => 'blub'
|
|
445
|
+
}
|
|
446
|
+
ensure
|
|
447
|
+
Committers::NeverCommitter.rollback_current_session
|
|
448
|
+
if session
|
|
449
|
+
session.left.execute "delete from extender_no_record"
|
|
450
|
+
session.right.execute "delete from extender_no_record"
|
|
451
|
+
session.left.execute "delete from rr_pending_changes"
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
it "replicate_difference should handle inserts failing due the new record being deleted after the original diff was loaded" do
|
|
457
|
+
begin
|
|
458
|
+
config = deep_copy(standard_config)
|
|
459
|
+
config.options[:committer] = :never_commit
|
|
460
|
+
|
|
461
|
+
session = Session.new(config)
|
|
462
|
+
|
|
463
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
464
|
+
'change_table' => 'extender_no_record',
|
|
465
|
+
'change_key' => 'id|1',
|
|
466
|
+
'change_type' => 'I',
|
|
467
|
+
'change_time' => Time.now
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
rep_run = ReplicationRun.new session
|
|
471
|
+
helper = ReplicationHelper.new(rep_run)
|
|
472
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
473
|
+
|
|
474
|
+
diff = ReplicationDifference.new session
|
|
475
|
+
diff.load
|
|
476
|
+
|
|
477
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
478
|
+
'change_table' => 'extender_no_record',
|
|
479
|
+
'change_key' => 'id|1',
|
|
480
|
+
'change_type' => 'D',
|
|
481
|
+
'change_time' => Time.now
|
|
482
|
+
}
|
|
483
|
+
replicator.replicate_difference diff, 2
|
|
484
|
+
|
|
485
|
+
# no rspec expectation: success is when we get till here without exception
|
|
486
|
+
ensure
|
|
487
|
+
Committers::NeverCommitter.rollback_current_session
|
|
488
|
+
session.left.execute "delete from rr_pending_changes" if session
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
it "replicate_difference should raise Exception if all replication attempts have been exceeded" do
|
|
493
|
+
rep_run = ReplicationRun.new Session.new
|
|
494
|
+
helper = ReplicationHelper.new(rep_run)
|
|
495
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
496
|
+
lambda {replicator.replicate_difference :dummy_diff, 0}.
|
|
497
|
+
should raise_error(Exception, "max replication attempts exceeded")
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
it "replicate_difference should handle updates failing due to the source record being deleted after the original diff was loaded" do
|
|
501
|
+
begin
|
|
502
|
+
config = deep_copy(standard_config)
|
|
503
|
+
config.options[:committer] = :never_commit
|
|
504
|
+
config.options[:replication_conflict_handling] = :left_wins
|
|
505
|
+
|
|
506
|
+
session = Session.new(config)
|
|
507
|
+
|
|
508
|
+
session.left.insert_record 'extender_no_record', {
|
|
509
|
+
'id' => '2',
|
|
510
|
+
'name' => 'bla'
|
|
511
|
+
}
|
|
512
|
+
session.right.insert_record 'extender_no_record', {
|
|
513
|
+
'id' => '2',
|
|
514
|
+
'name' => 'blub'
|
|
515
|
+
}
|
|
516
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
517
|
+
'change_table' => 'extender_no_record',
|
|
518
|
+
'change_key' => 'id|1',
|
|
519
|
+
'change_new_key' => 'id|2',
|
|
520
|
+
'change_type' => 'U',
|
|
521
|
+
'change_time' => Time.now
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
rep_run = ReplicationRun.new session
|
|
525
|
+
helper = ReplicationHelper.new(rep_run)
|
|
526
|
+
replicator = Replicators::TwoWayReplicator.new(helper)
|
|
527
|
+
|
|
528
|
+
diff = ReplicationDifference.new session
|
|
529
|
+
diff.load
|
|
530
|
+
|
|
531
|
+
session.left.delete_record 'extender_no_record', {'id' => '2'}
|
|
532
|
+
|
|
533
|
+
session.left.insert_record 'rr_pending_changes', {
|
|
534
|
+
'change_table' => 'extender_no_record',
|
|
535
|
+
'change_key' => 'id|2',
|
|
536
|
+
'change_type' => 'D',
|
|
537
|
+
'change_time' => Time.now
|
|
538
|
+
}
|
|
539
|
+
replicator.replicate_difference diff, 2
|
|
540
|
+
|
|
541
|
+
session.right.select_one("select * from extender_no_record").should be_nil
|
|
542
|
+
ensure
|
|
543
|
+
Committers::NeverCommitter.rollback_current_session
|
|
544
|
+
if session
|
|
545
|
+
session.left.execute "delete from extender_no_record"
|
|
546
|
+
session.right.execute "delete from extender_no_record"
|
|
547
|
+
session.left.execute "delete from rr_pending_changes"
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
end
|