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.
Files changed (125) hide show
  1. data/History.txt +83 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +151 -0
  4. data/README.txt +37 -0
  5. data/bin/rubyrep +8 -0
  6. data/lib/rubyrep.rb +72 -0
  7. data/lib/rubyrep/base_runner.rb +195 -0
  8. data/lib/rubyrep/command_runner.rb +144 -0
  9. data/lib/rubyrep/committers/buffered_committer.rb +151 -0
  10. data/lib/rubyrep/committers/committers.rb +152 -0
  11. data/lib/rubyrep/configuration.rb +275 -0
  12. data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
  13. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
  14. data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
  15. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
  16. data/lib/rubyrep/database_proxy.rb +52 -0
  17. data/lib/rubyrep/direct_table_scan.rb +75 -0
  18. data/lib/rubyrep/generate_runner.rb +105 -0
  19. data/lib/rubyrep/initializer.rb +39 -0
  20. data/lib/rubyrep/log_helper.rb +30 -0
  21. data/lib/rubyrep/logged_change.rb +160 -0
  22. data/lib/rubyrep/logged_change_loader.rb +197 -0
  23. data/lib/rubyrep/noisy_connection.rb +80 -0
  24. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  25. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  26. data/lib/rubyrep/proxy_connection.rb +431 -0
  27. data/lib/rubyrep/proxy_cursor.rb +44 -0
  28. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  29. data/lib/rubyrep/proxy_runner.rb +89 -0
  30. data/lib/rubyrep/replication_difference.rb +100 -0
  31. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  32. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
  33. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  34. data/lib/rubyrep/replication_helper.rb +142 -0
  35. data/lib/rubyrep/replication_initializer.rb +327 -0
  36. data/lib/rubyrep/replication_run.rb +142 -0
  37. data/lib/rubyrep/replication_runner.rb +166 -0
  38. data/lib/rubyrep/replicators/replicators.rb +42 -0
  39. data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
  40. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  41. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  42. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  43. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  44. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  45. data/lib/rubyrep/scan_runner.rb +25 -0
  46. data/lib/rubyrep/session.rb +230 -0
  47. data/lib/rubyrep/sync_helper.rb +121 -0
  48. data/lib/rubyrep/sync_runner.rb +31 -0
  49. data/lib/rubyrep/syncers/syncers.rb +112 -0
  50. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  51. data/lib/rubyrep/table_scan.rb +54 -0
  52. data/lib/rubyrep/table_scan_helper.rb +46 -0
  53. data/lib/rubyrep/table_sorter.rb +70 -0
  54. data/lib/rubyrep/table_spec_resolver.rb +142 -0
  55. data/lib/rubyrep/table_sync.rb +90 -0
  56. data/lib/rubyrep/task_sweeper.rb +77 -0
  57. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  58. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  59. data/lib/rubyrep/uninstall_runner.rb +93 -0
  60. data/lib/rubyrep/version.rb +9 -0
  61. data/rubyrep +8 -0
  62. data/rubyrep.bat +4 -0
  63. data/setup.rb +1585 -0
  64. data/spec/base_runner_spec.rb +218 -0
  65. data/spec/buffered_committer_spec.rb +274 -0
  66. data/spec/command_runner_spec.rb +145 -0
  67. data/spec/committers_spec.rb +178 -0
  68. data/spec/configuration_spec.rb +203 -0
  69. data/spec/connection_extender_interface_spec.rb +141 -0
  70. data/spec/connection_extenders_registration_spec.rb +164 -0
  71. data/spec/database_proxy_spec.rb +48 -0
  72. data/spec/database_rake_spec.rb +40 -0
  73. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  74. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  75. data/spec/direct_table_scan_spec.rb +61 -0
  76. data/spec/dolphins.jpg +0 -0
  77. data/spec/generate_runner_spec.rb +84 -0
  78. data/spec/initializer_spec.rb +46 -0
  79. data/spec/log_helper_spec.rb +39 -0
  80. data/spec/logged_change_loader_spec.rb +68 -0
  81. data/spec/logged_change_spec.rb +470 -0
  82. data/spec/noisy_connection_spec.rb +78 -0
  83. data/spec/postgresql_replication_spec.rb +48 -0
  84. data/spec/postgresql_schema_support_spec.rb +212 -0
  85. data/spec/postgresql_support_spec.rb +63 -0
  86. data/spec/progress_bar_spec.rb +77 -0
  87. data/spec/proxied_table_scan_spec.rb +151 -0
  88. data/spec/proxy_block_cursor_spec.rb +197 -0
  89. data/spec/proxy_connection_spec.rb +423 -0
  90. data/spec/proxy_cursor_spec.rb +56 -0
  91. data/spec/proxy_row_cursor_spec.rb +66 -0
  92. data/spec/proxy_runner_spec.rb +70 -0
  93. data/spec/replication_difference_spec.rb +161 -0
  94. data/spec/replication_extender_interface_spec.rb +367 -0
  95. data/spec/replication_extenders_spec.rb +32 -0
  96. data/spec/replication_helper_spec.rb +178 -0
  97. data/spec/replication_initializer_spec.rb +509 -0
  98. data/spec/replication_run_spec.rb +443 -0
  99. data/spec/replication_runner_spec.rb +254 -0
  100. data/spec/replicators_spec.rb +36 -0
  101. data/spec/rubyrep_spec.rb +8 -0
  102. data/spec/scan_detail_reporter_spec.rb +119 -0
  103. data/spec/scan_progress_printers_spec.rb +68 -0
  104. data/spec/scan_report_printers_spec.rb +67 -0
  105. data/spec/scan_runner_spec.rb +50 -0
  106. data/spec/scan_summary_reporter_spec.rb +61 -0
  107. data/spec/session_spec.rb +253 -0
  108. data/spec/spec.opts +1 -0
  109. data/spec/spec_helper.rb +305 -0
  110. data/spec/strange_name_support_spec.rb +135 -0
  111. data/spec/sync_helper_spec.rb +169 -0
  112. data/spec/sync_runner_spec.rb +78 -0
  113. data/spec/syncers_spec.rb +171 -0
  114. data/spec/table_scan_helper_spec.rb +36 -0
  115. data/spec/table_scan_spec.rb +49 -0
  116. data/spec/table_sorter_spec.rb +30 -0
  117. data/spec/table_spec_resolver_spec.rb +111 -0
  118. data/spec/table_sync_spec.rb +140 -0
  119. data/spec/task_sweeper_spec.rb +47 -0
  120. data/spec/trigger_mode_switcher_spec.rb +83 -0
  121. data/spec/two_way_replicator_spec.rb +721 -0
  122. data/spec/two_way_syncer_spec.rb +256 -0
  123. data/spec/type_casting_cursor_spec.rb +50 -0
  124. data/spec/uninstall_runner_spec.rb +93 -0
  125. metadata +190 -0
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ReplicationExtenders do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ @@old_cache_status = ConnectionExtenders.use_db_connection_cache(false)
9
+ end
10
+
11
+ after(:each) do
12
+ ConnectionExtenders.use_db_connection_cache(@@old_cache_status)
13
+ end
14
+
15
+ it "extenders should return list of registered connection extenders" do
16
+ ReplicationExtenders.extenders.include?(:postgresql).should be_true
17
+ end
18
+
19
+ it "register should register a new connection extender" do
20
+ ReplicationExtenders.register(:bla => :blub)
21
+
22
+ ReplicationExtenders.extenders.include?(:bla).should be_true
23
+ end
24
+
25
+ it "register should replace already existing connection extenders" do
26
+ ReplicationExtenders.register(:bla => :blub)
27
+ ReplicationExtenders.register(:bla => :blub2)
28
+
29
+ ReplicationExtenders.extenders[:bla].should == :blub2
30
+ end
31
+ end
32
+
@@ -0,0 +1,178 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ReplicationHelper do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should initialize the correct committer" do
11
+ session = Session.new
12
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
13
+ helper = ReplicationHelper.new(rep_run)
14
+ c = helper.instance_eval {@committer}
15
+ c.should be_an_instance_of(Committers::DefaultCommitter)
16
+ c.session.should == helper.session
17
+ end
18
+
19
+ it "session should return the session" do
20
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
21
+ helper = ReplicationHelper.new(rep_run)
22
+ helper.session.should == rep_run.session
23
+ end
24
+
25
+ it "type_cast should convert the row values correctly" do
26
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
27
+
28
+ helper = ReplicationHelper.new(rep_run)
29
+ helper.type_cast('scanner_records', 'id' => '1', 'name' => 'bla').
30
+ should == {'id' => 1, 'name' => 'bla'}
31
+
32
+ string_row = {
33
+ 'id' => '1',
34
+ 'decimal_test' => '1.234',
35
+ 'timestamp' => 'Sat Nov 10 20:15:01 +0900 2007',
36
+ 'binary_test' => "\004\b[\n\"\bbla:\ndummyi\006i\ai\b"
37
+ }
38
+ row = helper.type_cast('extender_type_check', string_row)
39
+ row.should == {
40
+ 'id' => 1,
41
+ 'decimal_test' => BigDecimal.new("1.234"),
42
+ 'timestamp' => Time.local(2007,"nov",10,20,15,1),
43
+ 'binary_test' => Marshal.dump(['bla',:dummy,1,2,3])
44
+ }
45
+ end
46
+
47
+ it "new_transaction? should delegate to the committer" do
48
+ session = Session.new
49
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
50
+ helper = ReplicationHelper.new(rep_run)
51
+ c = helper.instance_eval {@committer}
52
+ c.should_receive(:new_transaction?).and_return(true)
53
+ helper.new_transaction?.should be_true
54
+ end
55
+
56
+ it "replication_run should return the current ReplicationRun instance" do
57
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
58
+ helper = ReplicationHelper.new(rep_run)
59
+ helper.replication_run.should == rep_run
60
+ end
61
+
62
+ it "options should return the correct options" do
63
+ session = Session.new
64
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
65
+ helper = ReplicationHelper.new(rep_run)
66
+ helper.options.should == session.configuration.options
67
+ end
68
+
69
+ it "insert_record should insert the given record" do
70
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
71
+ helper = ReplicationHelper.new(rep_run)
72
+ c = helper.instance_eval {committer}
73
+ c.should_receive(:insert_record).with(:right, 'scanner_records', :dummy_record)
74
+ helper.insert_record :right, 'scanner_records', :dummy_record
75
+ end
76
+
77
+ it "update_record should update the given record" do
78
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
79
+ helper = ReplicationHelper.new(rep_run)
80
+ c = helper.instance_eval {committer}
81
+ c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, nil)
82
+ helper.update_record :right, 'scanner_records', :dummy_record
83
+ end
84
+
85
+ it "update_record should update the given record with the provided old key" do
86
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
87
+ helper = ReplicationHelper.new(rep_run)
88
+ c = helper.instance_eval {committer}
89
+ c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, :old_key)
90
+ helper.update_record :right, 'scanner_records', :dummy_record, :old_key
91
+ end
92
+
93
+ it "delete_record should delete the given record" do
94
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
95
+ helper = ReplicationHelper.new(rep_run)
96
+ c = helper.instance_eval {committer}
97
+ c.should_receive(:delete_record).with(:right, 'scanner_records', :dummy_record)
98
+ helper.delete_record :right, 'scanner_records', :dummy_record
99
+ end
100
+
101
+ it "load_record should load the specified record (values converted to original data types)" do
102
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
103
+ helper = ReplicationHelper.new(rep_run)
104
+ helper.load_record(:right, 'scanner_records', 'id' => '2').should == {
105
+ 'id' => 2, # Note: it's a number, not a string...
106
+ 'name' => 'Bob - right database version'
107
+ }
108
+ end
109
+
110
+ it "options_for_table should return the correct options for the table" do
111
+ Initializer.configuration.options = {:a => 1, :b => 2}
112
+ Initializer.configuration.add_table_options 'scanner_records', {:b => 3}
113
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
114
+ helper = ReplicationHelper.new(rep_run)
115
+ options = helper.options_for_table('scanner_records')
116
+ options[:a].should == 1
117
+ options[:b].should == 3
118
+ end
119
+
120
+ it "options_for_table should merge the configured options into the default two way replicator options" do
121
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
122
+ helper = ReplicationHelper.new(rep_run)
123
+ helper.options_for_table('scanner_records').include?(:left_change_handling).should be_true
124
+ helper.options_for_table('scanner_records').include?(:right_change_handling).should be_true
125
+ helper.options_for_table('scanner_records').include?(:replication_conflict_handling).should be_true
126
+ end
127
+
128
+ it "log_replication_outcome should log the replication outcome correctly" do
129
+ session = Session.new
130
+ session.left.begin_db_transaction
131
+ begin
132
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
133
+ helper = ReplicationHelper.new(rep_run)
134
+
135
+ loaders = LoggedChangeLoaders.new(session)
136
+
137
+ left_change = LoggedChange.new loaders[:left]
138
+ right_change = LoggedChange.new loaders[:right]
139
+ diff = ReplicationDifference.new loaders
140
+ diff.changes.replace :left => left_change, :right => right_change
141
+ diff.type = :conflict
142
+
143
+ left_change.type, right_change.type = :update, :delete
144
+ left_change.table = right_change.table = 'extender_combined_key'
145
+ left_change.key = right_change.key = {'first_id' => 1, 'second_id' => 2}
146
+
147
+ # Verify that the log information are made fitting
148
+ helper.should_receive(:fit_description_columns).
149
+ with('ignore', 'ignored').
150
+ and_return(['ignoreX', 'ignoredY'])
151
+
152
+ helper.log_replication_outcome diff, 'ignore', 'ignored'
153
+
154
+ row = session.left.select_one("select * from rr_logged_events order by id desc")
155
+ row['activity'].should == 'replication'
156
+ row['change_table'].should == 'extender_combined_key'
157
+ row['diff_type'].should == 'conflict'
158
+ row['change_key'].should == '"first_id"=>"1", "second_id"=>"2"'
159
+ row['left_change_type'].should == 'update'
160
+ row['right_change_type'].should == 'delete'
161
+ row['description'].should == 'ignoreX'
162
+ row['long_description'].should == 'ignoredY'
163
+ Time.parse(row['event_time']).should >= 10.seconds.ago
164
+ row['diff_dump'].should == diff.to_yaml
165
+ ensure
166
+ session.left.rollback_db_transaction if session
167
+ end
168
+ end
169
+
170
+ it "finalize should be delegated to the committer" do
171
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
172
+ helper = ReplicationHelper.new(rep_run)
173
+
174
+ c = helper.instance_eval {@committer}
175
+ c.should_receive(:finalize).with(false)
176
+ helper.finalize(false)
177
+ end
178
+ end
@@ -0,0 +1,509 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ReplicationInitializer do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ after(:each) do
11
+ end
12
+
13
+ it "initializer should store the session" do
14
+ session = Session.new
15
+ initializer = ReplicationInitializer.new session
16
+ initializer.session.should == session
17
+ end
18
+
19
+ it "options should return the table specific options if table is given" do
20
+ session = Session.new deep_copy(Initializer.configuration)
21
+ initializer = ReplicationInitializer.new session
22
+ session.configuration.should_receive(:options_for_table).
23
+ with('my_table').
24
+ and_return(:dummy_options)
25
+ initializer.options('my_table').should == :dummy_options
26
+ end
27
+
28
+ it "options should return the general options if no table is given" do
29
+ session = Session.new deep_copy(Initializer.configuration)
30
+ initializer = ReplicationInitializer.new session
31
+ session.configuration.should_receive(:options).
32
+ and_return(:dummy_options)
33
+ initializer.options.should == :dummy_options
34
+ end
35
+
36
+ it "create_trigger should create a working trigger" do
37
+ session = nil
38
+ begin
39
+ session = Session.new
40
+ session.left.begin_db_transaction
41
+ initializer = ReplicationInitializer.new(session)
42
+ initializer.create_trigger(:left, 'trigger_test')
43
+
44
+ session.left.insert_record 'trigger_test', {
45
+ 'first_id' => 1,
46
+ 'second_id' => 2,
47
+ 'name' => 'bla'
48
+ }
49
+
50
+ row = session.left.select_one("select * from rr_pending_changes")
51
+ row.delete 'id'
52
+ row.delete 'change_time'
53
+ row.should == {
54
+ 'change_table' => 'trigger_test',
55
+ 'change_key' => 'first_id|1|second_id|2',
56
+ 'change_new_key' => nil,
57
+ 'change_type' => 'I'
58
+ }
59
+ ensure
60
+ session.left.execute 'delete from trigger_test' if session
61
+ session.left.execute 'delete from rr_pending_changes' if session
62
+ session.left.rollback_db_transaction if session
63
+ end
64
+ end
65
+
66
+ it "trigger_exists? and drop_trigger should work correctly" do
67
+ session = nil
68
+ begin
69
+ session = Session.new
70
+ initializer = ReplicationInitializer.new(session)
71
+ if initializer.trigger_exists?(:left, 'trigger_test')
72
+ initializer.drop_trigger(:left, 'trigger_test')
73
+ end
74
+ session.left.begin_db_transaction
75
+
76
+ initializer.create_trigger :left, 'trigger_test'
77
+ initializer.trigger_exists?(:left, 'trigger_test').
78
+ should be_true
79
+ initializer.drop_trigger(:left, 'trigger_test')
80
+ initializer.trigger_exists?(:left, 'trigger_test').
81
+ should be_false
82
+ ensure
83
+ session.left.rollback_db_transaction if session
84
+ end
85
+ end
86
+
87
+ it "ensure_sequence_setup should not do anything if :adjust_sequences option is not given" do
88
+ config = deep_copy(Initializer.configuration)
89
+ config.add_table_options 'sequence_test', :adjust_sequences => false
90
+ session = Session.new(config)
91
+ initializer = ReplicationInitializer.new(session)
92
+
93
+ session.left.should_not_receive(:update_sequences)
94
+ session.right.should_not_receive(:update_sequences)
95
+
96
+ table_pair = {:left => 'sequence_test', :right => 'sequence_test'}
97
+ initializer.ensure_sequence_setup table_pair, 3, 2, 2
98
+ end
99
+
100
+ it "ensure_sequence_setup should ensure that a table's auto generated ID values have the correct increment and offset" do
101
+ session = nil
102
+ begin
103
+ session = Session.new
104
+ initializer = ReplicationInitializer.new(session)
105
+ session.left.begin_db_transaction
106
+ session.right.begin_db_transaction
107
+
108
+ session.left.execute "delete from sequence_test"
109
+ session.right.execute "delete from sequence_test"
110
+
111
+ # Note:
112
+ # Calling ensure_sequence_setup twice with different values to ensure that
113
+ # it is actually does something.
114
+
115
+ table_pair = {:left => 'sequence_test', :right => 'sequence_test'}
116
+
117
+ initializer.ensure_sequence_setup table_pair, 3, 2, 2
118
+ initializer.ensure_sequence_setup table_pair, 5, 2, 1
119
+ id1, id2 = get_example_sequence_values(session)
120
+ (id2 - id1).should == 5
121
+ (id1 % 5).should == 2
122
+ ensure
123
+ [:left, :right].each do |database|
124
+ initializer.clear_sequence_setup database, 'sequence_test' if session
125
+ session.send(database).execute "delete from sequence_test" if session
126
+ session.send(database).rollback_db_transaction if session
127
+ end
128
+ end
129
+ end
130
+
131
+ it "clear_sequence_setup should not do anything if :adjust_sequences option is not given" do
132
+ config = deep_copy(Initializer.configuration)
133
+ config.add_table_options 'sequence_test', :adjust_sequences => false
134
+ session = Session.new(config)
135
+ initializer = ReplicationInitializer.new(session)
136
+
137
+ session.left.should_not_receive(:clear_sequence_setup)
138
+
139
+ initializer.clear_sequence_setup :left, 'sequence_test'
140
+ end
141
+
142
+ it "clear_sequence_setup should remove custom sequence settings" do
143
+ session = nil
144
+ begin
145
+ session = Session.new
146
+ initializer = ReplicationInitializer.new(session)
147
+ session.left.begin_db_transaction
148
+ session.right.begin_db_transaction
149
+ table_pair = {:left => 'sequence_test', :right => 'sequence_test'}
150
+ initializer.ensure_sequence_setup table_pair, 5, 2, 2
151
+ initializer.clear_sequence_setup :left, 'sequence_test'
152
+ id1, id2 = get_example_sequence_values(session)
153
+ (id2 - id1).should == 1
154
+ ensure
155
+ [:left, :right].each do |database|
156
+ initializer.clear_sequence_setup database, 'sequence_test' if session
157
+ session.send(database).execute "delete from sequence_test" if session
158
+ session.send(database).rollback_db_transaction if session
159
+ end
160
+ end
161
+ end
162
+
163
+ it "change_log_exists? should return true if replication log exists" do
164
+ config = deep_copy(standard_config)
165
+ initializer = ReplicationInitializer.new(Session.new(config))
166
+ initializer.change_log_exists?(:left).should be_true
167
+ config.options[:rep_prefix] = 'r2'
168
+ initializer = ReplicationInitializer.new(Session.new(config))
169
+ initializer.change_log_exists?(:left).should be_false
170
+ end
171
+
172
+ it "event_log_exists? should return true if event log exists" do
173
+ config = deep_copy(standard_config)
174
+ initializer = ReplicationInitializer.new(Session.new(config))
175
+ initializer.event_log_exists?.should be_true
176
+ config.options[:rep_prefix] = 'r2'
177
+ initializer = ReplicationInitializer.new(Session.new(config))
178
+ initializer.event_log_exists?.should be_false
179
+ end
180
+
181
+ it "create_event_log / drop_event_log should create / drop the event log" do
182
+ config = deep_copy(standard_config)
183
+ config.options[:rep_prefix] = 'r2'
184
+ session = Session.new(config)
185
+ initializer = ReplicationInitializer.new(session)
186
+ initializer.drop_logged_events if initializer.event_log_exists?
187
+
188
+ $stderr.stub! :write
189
+ initializer.event_log_exists?.should be_false
190
+ initializer.create_event_log
191
+ initializer.event_log_exists?.should be_true
192
+
193
+ # verify that replication log has 8 byte, auto-generating primary key
194
+ session.left.insert_record 'r2_logged_events', {'id' => 1e18.to_i, 'change_key' => 'blub'}
195
+ session.left.select_one("select id from r2_logged_events where change_key = 'blub'")['id'].
196
+ to_i.should == 1e18.to_i
197
+
198
+ initializer.drop_event_log
199
+ initializer.event_log_exists?.should be_false
200
+ end
201
+
202
+ it "create_change_log / drop_change_log should create / drop the replication log" do
203
+ config = deep_copy(standard_config)
204
+ config.options[:rep_prefix] = 'r2'
205
+ session = Session.new(config)
206
+ initializer = ReplicationInitializer.new(session)
207
+ initializer.drop_change_log(:left) if initializer.change_log_exists?(:left)
208
+
209
+ $stderr.stub! :write
210
+ initializer.change_log_exists?(:left).should be_false
211
+ initializer.create_change_log(:left)
212
+ initializer.change_log_exists?(:left).should be_true
213
+
214
+ # verify that replication log has 8 byte, auto-generating primary key
215
+ session.left.insert_record 'r2_pending_changes', {'change_key' => 'bla'}
216
+ session.left.select_one("select id from r2_pending_changes where change_key = 'bla'")['id'].
217
+ to_i.should > 0
218
+ session.left.insert_record 'r2_pending_changes', {'id' => 1e18.to_i, 'change_key' => 'blub'}
219
+ session.left.select_one("select id from r2_pending_changes where change_key = 'blub'")['id'].
220
+ to_i.should == 1e18.to_i
221
+
222
+ initializer.drop_change_log(:left)
223
+ initializer.change_log_exists?(:left).should be_false
224
+ end
225
+
226
+ it "ensure_activity_markers should not create the tables if they already exist" do
227
+ session = Session.new
228
+ initializer = ReplicationInitializer.new(session)
229
+ session.left.should_not_receive(:create_table)
230
+ initializer.ensure_activity_markers
231
+ end
232
+
233
+ it "ensure_activity_markers should create the marker tables" do
234
+ begin
235
+ config = deep_copy(standard_config)
236
+ config.options[:rep_prefix] = 'rx'
237
+ session = Session.new(config)
238
+ initializer = ReplicationInitializer.new(session)
239
+ initializer.ensure_activity_markers
240
+ session.left.tables.include?('rx_running_flags').should be_true
241
+ session.right.tables.include?('rx_running_flags').should be_true
242
+
243
+ # right columns?
244
+ columns = session.left.columns('rx_running_flags')
245
+ columns.size.should == 1
246
+ columns[0].name.should == 'active'
247
+ ensure
248
+ if session
249
+ session.left.drop_table 'rx_running_flags'
250
+ session.right.drop_table 'rx_running_flags'
251
+ end
252
+ end
253
+ end
254
+
255
+ it "ensure_infrastructure should not create the infrastructure tables if they already exist" do
256
+ session = Session.new
257
+ initializer = ReplicationInitializer.new(session)
258
+ session.left.should_not_receive(:create_table)
259
+ initializer.ensure_infrastructure
260
+ end
261
+
262
+ it "drop_change_logs should drop the change_log tables" do
263
+ session = Session.new
264
+ initializer = ReplicationInitializer.new session
265
+ initializer.should_receive(:drop_change_log).with(:left)
266
+ initializer.should_receive(:drop_change_log).with(:right)
267
+
268
+ initializer.drop_change_logs
269
+ end
270
+
271
+ it "drop_change_logs should not do anything if change_log tables do not exist" do
272
+ config = deep_copy(standard_config)
273
+ config.options[:rep_prefix] = 'rx'
274
+ session = Session.new(config)
275
+ initializer = ReplicationInitializer.new session
276
+ initializer.should_not_receive(:drop_change_log).with(:left)
277
+ initializer.should_not_receive(:drop_change_log).with(:right)
278
+
279
+ initializer.drop_change_logs
280
+ end
281
+
282
+ it "drop_activity_markers should drop the activity_marker tables" do
283
+ session = Session.new
284
+ initializer = ReplicationInitializer.new session
285
+ session.left.should_receive(:drop_table).with('rr_running_flags')
286
+ session.right.should_receive(:drop_table).with('rr_running_flags')
287
+
288
+ initializer.drop_activity_markers
289
+ end
290
+
291
+ it "drop_activity_markers should not do anything if the activity_marker tables do not exist" do
292
+ config = deep_copy(standard_config)
293
+ config.options[:rep_prefix] = 'rx'
294
+ session = Session.new(config)
295
+ initializer = ReplicationInitializer.new session
296
+ session.left.should_not_receive(:drop_table).with('rr_running_flags')
297
+ session.right.should_not_receive(:drop_table).with('rr_running_flags')
298
+
299
+ initializer.drop_change_logs
300
+ end
301
+
302
+ it "drop_infrastructure should drop all infrastructure tables" do
303
+ session = Session.new
304
+ initializer = ReplicationInitializer.new session
305
+ initializer.should_receive(:drop_event_log)
306
+ initializer.should_receive(:drop_change_logs)
307
+ initializer.should_receive(:drop_activity_markers)
308
+
309
+ initializer.drop_infrastructure
310
+ end
311
+
312
+ it "ensure_change_logs should create the change_log tables" do
313
+ session = nil
314
+ begin
315
+ config = deep_copy(standard_config)
316
+ config.options[:rep_prefix] = 'rx'
317
+ session = Session.new(config)
318
+ initializer = ReplicationInitializer.new(session)
319
+ initializer.ensure_change_logs
320
+ ensure
321
+ if session
322
+ session.left.drop_table 'rx_pending_changes'
323
+ session.right.drop_table 'rx_pending_changes'
324
+ end
325
+ end
326
+ end
327
+
328
+ it "ensure_change_logs should do nothing if the change_log tables already exist" do
329
+ session = Session.new
330
+ initializer = ReplicationInitializer.new session
331
+ initializer.should_not_receive(:create_change_log)
332
+
333
+ initializer.ensure_change_logs
334
+ end
335
+
336
+ it "ensure_event_log should create the event_log table" do
337
+ session = nil
338
+ begin
339
+ config = deep_copy(standard_config)
340
+ config.options[:rep_prefix] = 'rx'
341
+ session = Session.new(config)
342
+ initializer = ReplicationInitializer.new(session)
343
+ initializer.ensure_event_log
344
+ ensure
345
+ session.left.drop_table 'rx_logged_events' if session
346
+ end
347
+ end
348
+
349
+ it "ensure_event_log should do nothing if the event_log table already exist" do
350
+ session = Session.new
351
+ initializer = ReplicationInitializer.new session
352
+ initializer.should_not_receive(:create_event_log)
353
+
354
+ initializer.ensure_event_log
355
+ end
356
+
357
+ it "ensure_infrastructure should create the infrastructure tables" do
358
+ session = Session.new
359
+ initializer = ReplicationInitializer.new(session)
360
+ initializer.should_receive :ensure_activity_markers
361
+ initializer.should_receive :ensure_change_logs
362
+ initializer.should_receive :ensure_event_log
363
+ initializer.ensure_infrastructure
364
+ end
365
+
366
+ it "call_after_init_handler should call the according handler" do
367
+ config = deep_copy(standard_config)
368
+ received_session = nil
369
+ config.options[:after_infrastructure_setup] = lambda do |session|
370
+ received_session = session
371
+ end
372
+ session = Session.new config
373
+ initializer = ReplicationInitializer.new session
374
+ initializer.call_after_infrastructure_setup_handler
375
+
376
+ received_session.should == session
377
+ end
378
+
379
+ it "exclude_ruby_rep_tables should exclude the correct system tables" do
380
+ config = deep_copy(standard_config)
381
+ initializer = ReplicationInitializer.new(Session.new(config))
382
+ initializer.exclude_rubyrep_tables
383
+ initializer.session.configuration.excluded_table_specs.include?(/^rr_.*/).should be_true
384
+ end
385
+
386
+ it "restore_unconfigured_tables should remove triggers and sequences setups of unconfigured tables" do
387
+ session = Session.new
388
+ initializer = ReplicationInitializer.new session
389
+ begin
390
+ ['scanner_left_records_only', 'scanner_records'].each do |table|
391
+ initializer.create_trigger(:left, table)
392
+ initializer.create_trigger(:right, table)
393
+ initializer.ensure_sequence_setup(
394
+ {:left => table, :right => table},
395
+ 2, 0, 1
396
+ )
397
+ session.right.insert_record table, {'id' => 100, 'name' => 'bla'}
398
+ end
399
+
400
+ # verify that the unconfigured tables are restored and pending changes deleted
401
+ initializer.restore_unconfigured_tables
402
+ initializer.trigger_exists?(:right, 'scanner_records').should be_false
403
+ session.right.sequence_values('rr', 'scanner_records').values[0][:increment].should == 1
404
+ session.right.select_one("select * from rr_pending_changes where change_table = 'scanner_records'").should be_nil
405
+
406
+ # verify that the configured tables are not touched
407
+ initializer.trigger_exists?(:right, 'scanner_left_records_only').should be_true
408
+ session.right.sequence_values('rr', 'scanner_left_records_only').values[0][:increment].should == 2
409
+ session.right.select_one("select * from rr_pending_changes where change_table = 'scanner_left_records_only'").should_not be_nil
410
+ ensure
411
+ ['scanner_left_records_only', 'scanner_records'].each do |table|
412
+ [:left, :right].each do |database|
413
+ if initializer.trigger_exists?(database, table)
414
+ initializer.drop_trigger(database, table)
415
+ end
416
+ initializer.clear_sequence_setup database, table
417
+ end
418
+ session.right.delete_record table, {'id' => 100}
419
+ end
420
+ session.right.execute "delete from rr_pending_changes"
421
+ end
422
+ end
423
+
424
+ it "prepare_replication should prepare the replication" do
425
+ session = nil
426
+ initializer = nil
427
+ org_stdout = $stdout
428
+
429
+ config = deep_copy(standard_config)
430
+ config.options[:committer] = :buffered_commit
431
+ config.options[:use_ansi] = false
432
+
433
+ received_session = nil
434
+ config.options[:after_infrastructure_setup] = lambda do |session|
435
+ received_session = session
436
+ end
437
+
438
+ config.include_tables 'rr_pending_changes' # added to verify that it is ignored
439
+
440
+ # added to verify that a disabled :initial_sync is honored
441
+ config.add_table_options 'table_with_manual_key', :initial_sync => false
442
+
443
+ session = Session.new(config)
444
+
445
+ # dummy data to verify that 'table_with_manual_key' is indeed not synced
446
+ session.left.insert_record 'table_with_manual_key', :id => 1, :name => 'bla'
447
+
448
+ $stdout = StringIO.new
449
+ begin
450
+ initializer = ReplicationInitializer.new(session)
451
+ initializer.should_receive(:ensure_infrastructure).any_number_of_times
452
+ initializer.should_receive(:restore_unconfigured_tables).any_number_of_times
453
+ initializer.prepare_replication
454
+
455
+ received_session.should == session
456
+
457
+ # verify sequences have been setup
458
+ session.left.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
459
+ session.right.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
460
+
461
+ # verify table was synced
462
+ left_records = session.left.select_all("select * from scanner_left_records_only order by id")
463
+ right_records = session.left.select_all("select * from scanner_left_records_only order by id")
464
+ left_records.should == right_records
465
+
466
+ # verify rubyrep activity is _not_ logged
467
+ session.right.select_all("select * from rr_pending_changes").should be_empty
468
+
469
+ # verify other data changes are logged
470
+ initializer.trigger_exists?(:left, 'scanner_left_records_only').should be_true
471
+ session.left.insert_record 'scanner_left_records_only', {'id' => 10, 'name' => 'bla'}
472
+ changes = session.left.select_all("select change_key from rr_pending_changes")
473
+ changes.size.should == 1
474
+ changes[0]['change_key'].should == 'id|10'
475
+
476
+ # verify that the 'rr_pending_changes' table was not touched
477
+ initializer.trigger_exists?(:left, 'rr_pending_changes').should be_false
478
+
479
+ # verify that :initial_sync => false is honored
480
+ session.right.select_all("select * from table_with_manual_key").should be_empty
481
+
482
+ # verify that syncing is done only for unsynced tables
483
+ SyncRunner.should_not_receive(:new)
484
+ initializer.prepare_replication
485
+
486
+ ensure
487
+ $stdout = org_stdout
488
+ if session
489
+ session.left.execute "delete from table_with_manual_key"
490
+ session.left.execute "delete from scanner_left_records_only where id = 10"
491
+ session.right.execute "delete from scanner_left_records_only"
492
+ [:left, :right].each do |database|
493
+ session.send(database).execute "delete from rr_pending_changes"
494
+ end
495
+ end
496
+ if initializer
497
+ [:left, :right].each do |database|
498
+ initializer.clear_sequence_setup database, 'scanner_left_records_only'
499
+ initializer.clear_sequence_setup database, 'table_with_manual_key'
500
+ ['scanner_left_records_only', 'table_with_manual_key'].each do |table|
501
+ if initializer.trigger_exists?(database, table)
502
+ initializer.drop_trigger database, table
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end
508
+ end
509
+ end