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.
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