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.
- data/History.txt +83 -0
- data/License.txt +20 -0
- data/Manifest.txt +151 -0
- data/README.txt +37 -0
- data/bin/rubyrep +8 -0
- data/lib/rubyrep.rb +72 -0
- data/lib/rubyrep/base_runner.rb +195 -0
- data/lib/rubyrep/command_runner.rb +144 -0
- data/lib/rubyrep/committers/buffered_committer.rb +151 -0
- data/lib/rubyrep/committers/committers.rb +152 -0
- data/lib/rubyrep/configuration.rb +275 -0
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
- data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
- data/lib/rubyrep/database_proxy.rb +52 -0
- data/lib/rubyrep/direct_table_scan.rb +75 -0
- data/lib/rubyrep/generate_runner.rb +105 -0
- data/lib/rubyrep/initializer.rb +39 -0
- data/lib/rubyrep/log_helper.rb +30 -0
- data/lib/rubyrep/logged_change.rb +160 -0
- data/lib/rubyrep/logged_change_loader.rb +197 -0
- data/lib/rubyrep/noisy_connection.rb +80 -0
- data/lib/rubyrep/proxied_table_scan.rb +171 -0
- data/lib/rubyrep/proxy_block_cursor.rb +145 -0
- data/lib/rubyrep/proxy_connection.rb +431 -0
- data/lib/rubyrep/proxy_cursor.rb +44 -0
- data/lib/rubyrep/proxy_row_cursor.rb +43 -0
- data/lib/rubyrep/proxy_runner.rb +89 -0
- data/lib/rubyrep/replication_difference.rb +100 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
- data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
- data/lib/rubyrep/replication_helper.rb +142 -0
- data/lib/rubyrep/replication_initializer.rb +327 -0
- data/lib/rubyrep/replication_run.rb +142 -0
- data/lib/rubyrep/replication_runner.rb +166 -0
- data/lib/rubyrep/replicators/replicators.rb +42 -0
- data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
- data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
- data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
- data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
- data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
- data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
- data/lib/rubyrep/scan_runner.rb +25 -0
- data/lib/rubyrep/session.rb +230 -0
- data/lib/rubyrep/sync_helper.rb +121 -0
- data/lib/rubyrep/sync_runner.rb +31 -0
- data/lib/rubyrep/syncers/syncers.rb +112 -0
- data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
- data/lib/rubyrep/table_scan.rb +54 -0
- data/lib/rubyrep/table_scan_helper.rb +46 -0
- data/lib/rubyrep/table_sorter.rb +70 -0
- data/lib/rubyrep/table_spec_resolver.rb +142 -0
- data/lib/rubyrep/table_sync.rb +90 -0
- data/lib/rubyrep/task_sweeper.rb +77 -0
- data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
- data/lib/rubyrep/type_casting_cursor.rb +31 -0
- data/lib/rubyrep/uninstall_runner.rb +93 -0
- data/lib/rubyrep/version.rb +9 -0
- data/rubyrep +8 -0
- data/rubyrep.bat +4 -0
- data/setup.rb +1585 -0
- data/spec/base_runner_spec.rb +218 -0
- data/spec/buffered_committer_spec.rb +274 -0
- data/spec/command_runner_spec.rb +145 -0
- data/spec/committers_spec.rb +178 -0
- data/spec/configuration_spec.rb +203 -0
- data/spec/connection_extender_interface_spec.rb +141 -0
- data/spec/connection_extenders_registration_spec.rb +164 -0
- data/spec/database_proxy_spec.rb +48 -0
- data/spec/database_rake_spec.rb +40 -0
- data/spec/db_specific_connection_extenders_spec.rb +34 -0
- data/spec/db_specific_replication_extenders_spec.rb +38 -0
- data/spec/direct_table_scan_spec.rb +61 -0
- data/spec/dolphins.jpg +0 -0
- data/spec/generate_runner_spec.rb +84 -0
- data/spec/initializer_spec.rb +46 -0
- data/spec/log_helper_spec.rb +39 -0
- data/spec/logged_change_loader_spec.rb +68 -0
- data/spec/logged_change_spec.rb +470 -0
- data/spec/noisy_connection_spec.rb +78 -0
- data/spec/postgresql_replication_spec.rb +48 -0
- data/spec/postgresql_schema_support_spec.rb +212 -0
- data/spec/postgresql_support_spec.rb +63 -0
- data/spec/progress_bar_spec.rb +77 -0
- data/spec/proxied_table_scan_spec.rb +151 -0
- data/spec/proxy_block_cursor_spec.rb +197 -0
- data/spec/proxy_connection_spec.rb +423 -0
- data/spec/proxy_cursor_spec.rb +56 -0
- data/spec/proxy_row_cursor_spec.rb +66 -0
- data/spec/proxy_runner_spec.rb +70 -0
- data/spec/replication_difference_spec.rb +161 -0
- data/spec/replication_extender_interface_spec.rb +367 -0
- data/spec/replication_extenders_spec.rb +32 -0
- data/spec/replication_helper_spec.rb +178 -0
- data/spec/replication_initializer_spec.rb +509 -0
- data/spec/replication_run_spec.rb +443 -0
- data/spec/replication_runner_spec.rb +254 -0
- data/spec/replicators_spec.rb +36 -0
- data/spec/rubyrep_spec.rb +8 -0
- data/spec/scan_detail_reporter_spec.rb +119 -0
- data/spec/scan_progress_printers_spec.rb +68 -0
- data/spec/scan_report_printers_spec.rb +67 -0
- data/spec/scan_runner_spec.rb +50 -0
- data/spec/scan_summary_reporter_spec.rb +61 -0
- data/spec/session_spec.rb +253 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +305 -0
- data/spec/strange_name_support_spec.rb +135 -0
- data/spec/sync_helper_spec.rb +169 -0
- data/spec/sync_runner_spec.rb +78 -0
- data/spec/syncers_spec.rb +171 -0
- data/spec/table_scan_helper_spec.rb +36 -0
- data/spec/table_scan_spec.rb +49 -0
- data/spec/table_sorter_spec.rb +30 -0
- data/spec/table_spec_resolver_spec.rb +111 -0
- data/spec/table_sync_spec.rb +140 -0
- data/spec/task_sweeper_spec.rb +47 -0
- data/spec/trigger_mode_switcher_spec.rb +83 -0
- data/spec/two_way_replicator_spec.rb +721 -0
- data/spec/two_way_syncer_spec.rb +256 -0
- data/spec/type_casting_cursor_spec.rb +50 -0
- data/spec/uninstall_runner_spec.rb +93 -0
- 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
|