rubyrep 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +137 -0
  4. data/README.txt +37 -0
  5. data/Rakefile +30 -0
  6. data/bin/rubyrep +8 -0
  7. data/config/hoe.rb +72 -0
  8. data/config/mysql_config.rb +25 -0
  9. data/config/postgres_config.rb +21 -0
  10. data/config/proxied_test_config.rb +14 -0
  11. data/config/redmine_config.rb +17 -0
  12. data/config/rep_config.rb +20 -0
  13. data/config/requirements.rb +32 -0
  14. data/config/test_config.rb +20 -0
  15. data/lib/rubyrep/base_runner.rb +195 -0
  16. data/lib/rubyrep/command_runner.rb +144 -0
  17. data/lib/rubyrep/committers/buffered_committer.rb +140 -0
  18. data/lib/rubyrep/committers/committers.rb +146 -0
  19. data/lib/rubyrep/configuration.rb +240 -0
  20. data/lib/rubyrep/connection_extenders/connection_extenders.rb +133 -0
  21. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +284 -0
  22. data/lib/rubyrep/connection_extenders/mysql_extender.rb +168 -0
  23. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +261 -0
  24. data/lib/rubyrep/database_proxy.rb +52 -0
  25. data/lib/rubyrep/direct_table_scan.rb +75 -0
  26. data/lib/rubyrep/generate_runner.rb +105 -0
  27. data/lib/rubyrep/initializer.rb +39 -0
  28. data/lib/rubyrep/logged_change.rb +326 -0
  29. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  30. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  31. data/lib/rubyrep/proxy_connection.rb +318 -0
  32. data/lib/rubyrep/proxy_cursor.rb +44 -0
  33. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  34. data/lib/rubyrep/proxy_runner.rb +89 -0
  35. data/lib/rubyrep/replication_difference.rb +91 -0
  36. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  37. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +204 -0
  38. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  39. data/lib/rubyrep/replication_helper.rb +104 -0
  40. data/lib/rubyrep/replication_initializer.rb +307 -0
  41. data/lib/rubyrep/replication_run.rb +48 -0
  42. data/lib/rubyrep/replication_runner.rb +138 -0
  43. data/lib/rubyrep/replicators/replicators.rb +37 -0
  44. data/lib/rubyrep/replicators/two_way_replicator.rb +334 -0
  45. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  46. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  47. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  48. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  49. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  50. data/lib/rubyrep/scan_runner.rb +25 -0
  51. data/lib/rubyrep/session.rb +177 -0
  52. data/lib/rubyrep/sync_helper.rb +111 -0
  53. data/lib/rubyrep/sync_runner.rb +31 -0
  54. data/lib/rubyrep/syncers/syncers.rb +112 -0
  55. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  56. data/lib/rubyrep/table_scan.rb +54 -0
  57. data/lib/rubyrep/table_scan_helper.rb +38 -0
  58. data/lib/rubyrep/table_sorter.rb +70 -0
  59. data/lib/rubyrep/table_spec_resolver.rb +136 -0
  60. data/lib/rubyrep/table_sync.rb +68 -0
  61. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  62. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  63. data/lib/rubyrep/uninstall_runner.rb +92 -0
  64. data/lib/rubyrep/version.rb +9 -0
  65. data/lib/rubyrep.rb +68 -0
  66. data/script/destroy +14 -0
  67. data/script/generate +14 -0
  68. data/script/txt2html +74 -0
  69. data/setup.rb +1585 -0
  70. data/sims/performance/big_rep_spec.rb +100 -0
  71. data/sims/performance/big_scan_spec.rb +57 -0
  72. data/sims/performance/big_sync_spec.rb +141 -0
  73. data/sims/performance/performance.rake +228 -0
  74. data/sims/sim_helper.rb +24 -0
  75. data/spec/base_runner_spec.rb +218 -0
  76. data/spec/buffered_committer_spec.rb +271 -0
  77. data/spec/command_runner_spec.rb +145 -0
  78. data/spec/committers_spec.rb +174 -0
  79. data/spec/configuration_spec.rb +198 -0
  80. data/spec/connection_extender_interface_spec.rb +138 -0
  81. data/spec/connection_extenders_registration_spec.rb +129 -0
  82. data/spec/database_proxy_spec.rb +48 -0
  83. data/spec/database_rake_spec.rb +40 -0
  84. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  85. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  86. data/spec/direct_table_scan_spec.rb +61 -0
  87. data/spec/generate_runner_spec.rb +84 -0
  88. data/spec/initializer_spec.rb +46 -0
  89. data/spec/logged_change_spec.rb +480 -0
  90. data/spec/postgresql_replication_spec.rb +48 -0
  91. data/spec/postgresql_support_spec.rb +57 -0
  92. data/spec/progress_bar_spec.rb +77 -0
  93. data/spec/proxied_table_scan_spec.rb +151 -0
  94. data/spec/proxy_block_cursor_spec.rb +197 -0
  95. data/spec/proxy_connection_spec.rb +399 -0
  96. data/spec/proxy_cursor_spec.rb +56 -0
  97. data/spec/proxy_row_cursor_spec.rb +66 -0
  98. data/spec/proxy_runner_spec.rb +70 -0
  99. data/spec/replication_difference_spec.rb +160 -0
  100. data/spec/replication_extender_interface_spec.rb +365 -0
  101. data/spec/replication_extenders_spec.rb +32 -0
  102. data/spec/replication_helper_spec.rb +121 -0
  103. data/spec/replication_initializer_spec.rb +477 -0
  104. data/spec/replication_run_spec.rb +166 -0
  105. data/spec/replication_runner_spec.rb +213 -0
  106. data/spec/replicators_spec.rb +31 -0
  107. data/spec/rubyrep_spec.rb +8 -0
  108. data/spec/scan_detail_reporter_spec.rb +119 -0
  109. data/spec/scan_progress_printers_spec.rb +68 -0
  110. data/spec/scan_report_printers_spec.rb +67 -0
  111. data/spec/scan_runner_spec.rb +50 -0
  112. data/spec/scan_summary_reporter_spec.rb +61 -0
  113. data/spec/session_spec.rb +212 -0
  114. data/spec/spec.opts +1 -0
  115. data/spec/spec_helper.rb +295 -0
  116. data/spec/sync_helper_spec.rb +157 -0
  117. data/spec/sync_runner_spec.rb +78 -0
  118. data/spec/syncers_spec.rb +171 -0
  119. data/spec/table_scan_helper_spec.rb +29 -0
  120. data/spec/table_scan_spec.rb +49 -0
  121. data/spec/table_sorter_spec.rb +31 -0
  122. data/spec/table_spec_resolver_spec.rb +102 -0
  123. data/spec/table_sync_spec.rb +84 -0
  124. data/spec/trigger_mode_switcher_spec.rb +83 -0
  125. data/spec/two_way_replicator_spec.rb +551 -0
  126. data/spec/two_way_syncer_spec.rb +256 -0
  127. data/spec/type_casting_cursor_spec.rb +50 -0
  128. data/spec/uninstall_runner_spec.rb +86 -0
  129. data/tasks/database.rake +439 -0
  130. data/tasks/deployment.rake +29 -0
  131. data/tasks/environment.rake +9 -0
  132. data/tasks/java.rake +37 -0
  133. data/tasks/redmine_test.rake +47 -0
  134. data/tasks/rspec.rake +68 -0
  135. data/tasks/rubyrep.tailor +18 -0
  136. data/tasks/stats.rake +19 -0
  137. data/tasks/task_helper.rb +20 -0
  138. data.tar.gz.sig +0 -0
  139. metadata +243 -0
  140. 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