andyjeffries-rubyrep 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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