rubyrep 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +137 -0
- data/README.txt +37 -0
- data/Rakefile +30 -0
- data/bin/rubyrep +8 -0
- data/config/hoe.rb +72 -0
- data/config/mysql_config.rb +25 -0
- data/config/postgres_config.rb +21 -0
- data/config/proxied_test_config.rb +14 -0
- data/config/redmine_config.rb +17 -0
- data/config/rep_config.rb +20 -0
- data/config/requirements.rb +32 -0
- data/config/test_config.rb +20 -0
- data/lib/rubyrep/base_runner.rb +195 -0
- data/lib/rubyrep/command_runner.rb +144 -0
- data/lib/rubyrep/committers/buffered_committer.rb +140 -0
- data/lib/rubyrep/committers/committers.rb +146 -0
- data/lib/rubyrep/configuration.rb +240 -0
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +133 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +284 -0
- data/lib/rubyrep/connection_extenders/mysql_extender.rb +168 -0
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +261 -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/logged_change.rb +326 -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 +318 -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 +91 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +204 -0
- data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
- data/lib/rubyrep/replication_helper.rb +104 -0
- data/lib/rubyrep/replication_initializer.rb +307 -0
- data/lib/rubyrep/replication_run.rb +48 -0
- data/lib/rubyrep/replication_runner.rb +138 -0
- data/lib/rubyrep/replicators/replicators.rb +37 -0
- data/lib/rubyrep/replicators/two_way_replicator.rb +334 -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 +177 -0
- data/lib/rubyrep/sync_helper.rb +111 -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 +38 -0
- data/lib/rubyrep/table_sorter.rb +70 -0
- data/lib/rubyrep/table_spec_resolver.rb +136 -0
- data/lib/rubyrep/table_sync.rb +68 -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 +92 -0
- data/lib/rubyrep/version.rb +9 -0
- data/lib/rubyrep.rb +68 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/sims/performance/big_rep_spec.rb +100 -0
- data/sims/performance/big_scan_spec.rb +57 -0
- data/sims/performance/big_sync_spec.rb +141 -0
- data/sims/performance/performance.rake +228 -0
- data/sims/sim_helper.rb +24 -0
- data/spec/base_runner_spec.rb +218 -0
- data/spec/buffered_committer_spec.rb +271 -0
- data/spec/command_runner_spec.rb +145 -0
- data/spec/committers_spec.rb +174 -0
- data/spec/configuration_spec.rb +198 -0
- data/spec/connection_extender_interface_spec.rb +138 -0
- data/spec/connection_extenders_registration_spec.rb +129 -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/generate_runner_spec.rb +84 -0
- data/spec/initializer_spec.rb +46 -0
- data/spec/logged_change_spec.rb +480 -0
- data/spec/postgresql_replication_spec.rb +48 -0
- data/spec/postgresql_support_spec.rb +57 -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 +399 -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 +160 -0
- data/spec/replication_extender_interface_spec.rb +365 -0
- data/spec/replication_extenders_spec.rb +32 -0
- data/spec/replication_helper_spec.rb +121 -0
- data/spec/replication_initializer_spec.rb +477 -0
- data/spec/replication_run_spec.rb +166 -0
- data/spec/replication_runner_spec.rb +213 -0
- data/spec/replicators_spec.rb +31 -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 +212 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +295 -0
- data/spec/sync_helper_spec.rb +157 -0
- data/spec/sync_runner_spec.rb +78 -0
- data/spec/syncers_spec.rb +171 -0
- data/spec/table_scan_helper_spec.rb +29 -0
- data/spec/table_scan_spec.rb +49 -0
- data/spec/table_sorter_spec.rb +31 -0
- data/spec/table_spec_resolver_spec.rb +102 -0
- data/spec/table_sync_spec.rb +84 -0
- data/spec/trigger_mode_switcher_spec.rb +83 -0
- data/spec/two_way_replicator_spec.rb +551 -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 +86 -0
- data/tasks/database.rake +439 -0
- data/tasks/deployment.rake +29 -0
- data/tasks/environment.rake +9 -0
- data/tasks/java.rake +37 -0
- data/tasks/redmine_test.rake +47 -0
- data/tasks/rspec.rake +68 -0
- data/tasks/rubyrep.tailor +18 -0
- data/tasks/stats.rake +19 -0
- data/tasks/task_helper.rb +20 -0
- data.tar.gz.sig +0 -0
- metadata +243 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe SyncRunner do
|
|
6
|
+
before(:each) do
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should register itself with CommandRunner" do
|
|
10
|
+
CommandRunner.commands['sync'][:command].should == SyncRunner
|
|
11
|
+
CommandRunner.commands['sync'][:description].should be_an_instance_of(String)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "prepare_table_pairs should sort the tables" do
|
|
15
|
+
session = Session.new standard_config
|
|
16
|
+
session.should_receive(:sort_table_pairs).
|
|
17
|
+
with(:dummy_table_pairs).
|
|
18
|
+
and_return(:sorted_dummy_table_pairs)
|
|
19
|
+
|
|
20
|
+
sync_runner = SyncRunner.new
|
|
21
|
+
sync_runner.stub!(:session).and_return(session)
|
|
22
|
+
|
|
23
|
+
sync_runner.prepare_table_pairs(:dummy_table_pairs).should == :sorted_dummy_table_pairs
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "execute should sync the specified tables" do
|
|
27
|
+
org_stdout = $stdout
|
|
28
|
+
session = nil
|
|
29
|
+
|
|
30
|
+
# This is necessary to avoid the cached RubyRep configurations from getting
|
|
31
|
+
# overwritten by the sync run
|
|
32
|
+
old_config, Initializer.configuration = Initializer.configuration, Configuration.new
|
|
33
|
+
|
|
34
|
+
session = Session.new(standard_config)
|
|
35
|
+
session.left.begin_db_transaction
|
|
36
|
+
session.right.begin_db_transaction
|
|
37
|
+
|
|
38
|
+
$stdout = StringIO.new
|
|
39
|
+
begin
|
|
40
|
+
sync_runner = SyncRunner.new
|
|
41
|
+
sync_runner.options = {
|
|
42
|
+
:config_file => "#{File.dirname(__FILE__)}/../config/test_config.rb",
|
|
43
|
+
:table_specs => ["scanner_records"]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
sync_runner.execute
|
|
47
|
+
|
|
48
|
+
$stdout.string.should =~
|
|
49
|
+
/scanner_records .* 5\n/
|
|
50
|
+
|
|
51
|
+
left_records = session.left.connection.select_all("select * from scanner_records order by id")
|
|
52
|
+
right_records = session.right.connection.select_all("select * from scanner_records order by id")
|
|
53
|
+
left_records.should == right_records
|
|
54
|
+
ensure
|
|
55
|
+
$stdout = org_stdout
|
|
56
|
+
Initializer.configuration = old_config if old_config
|
|
57
|
+
if session
|
|
58
|
+
session.left.rollback_db_transaction
|
|
59
|
+
session.right.rollback_db_transaction
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "create_processor should create the TableSync instance" do
|
|
65
|
+
TableSync.should_receive(:new).
|
|
66
|
+
with(:dummy_session, "left_table", "right_table").
|
|
67
|
+
and_return(:dummy_table_sync)
|
|
68
|
+
sync_runner = SyncRunner.new
|
|
69
|
+
sync_runner.should_receive(:session).and_return(:dummy_session)
|
|
70
|
+
sync_runner.create_processor("left_table", "right_table").
|
|
71
|
+
should == :dummy_table_sync
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "summary_description should return a description" do
|
|
75
|
+
SyncRunner.new.summary_description.should be_an_instance_of(String)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe Syncers do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@old_syncers = Syncers.syncers
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:each) do
|
|
11
|
+
Syncers.instance_variable_set :@syncers, @old_syncers
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "syncers should return empty hash if nil" do
|
|
15
|
+
Syncers.instance_variable_set :@syncers, nil
|
|
16
|
+
Syncers.syncers.should == {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "syncers should return the registered syncers" do
|
|
20
|
+
Syncers.instance_variable_set :@syncers, :dummy_data
|
|
21
|
+
Syncers.syncers.should == :dummy_data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "configured_syncer should return the correct syncer as per :syncer option, if both :syncer and :replicator is configured" do
|
|
25
|
+
options = {
|
|
26
|
+
:syncer => :two_way,
|
|
27
|
+
:replicator => :key2
|
|
28
|
+
}
|
|
29
|
+
Syncers.configured_syncer(options).should == Syncers::TwoWaySyncer
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "configured_syncer should return the correct syncer as per :replicator option if no :syncer option is provided" do
|
|
33
|
+
options = {:replicator => :two_way}
|
|
34
|
+
Syncers.configured_syncer(options).should == Syncers::TwoWaySyncer
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "register should register the provided commiter" do
|
|
38
|
+
Syncers.instance_variable_set :@syncers, nil
|
|
39
|
+
Syncers.register :a_key => :a
|
|
40
|
+
Syncers.register :b_key => :b
|
|
41
|
+
Syncers.syncers[:a_key].should == :a
|
|
42
|
+
Syncers.syncers[:b_key].should == :b
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe Syncers::OneWaySyncer do
|
|
47
|
+
before(:each) do
|
|
48
|
+
Initializer.configuration = standard_config
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should register itself" do
|
|
52
|
+
Syncers::syncers[:one_way].should == Syncers::OneWaySyncer
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "initialize should store sync_helper" do
|
|
56
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
57
|
+
helper = SyncHelper.new(sync)
|
|
58
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
59
|
+
syncer.sync_helper.should == helper
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "initialize should calculate course source, target and source_record_index" do
|
|
63
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
64
|
+
helper = SyncHelper.new(sync)
|
|
65
|
+
|
|
66
|
+
# verify correct behaviour if syncing to the left
|
|
67
|
+
helper.stub!(:sync_options).and_return({:direction => :left})
|
|
68
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
69
|
+
syncer.source.should == :right
|
|
70
|
+
syncer.target.should == :left
|
|
71
|
+
syncer.source_record_index.should == 1
|
|
72
|
+
|
|
73
|
+
# verify correct behaviour if syncing to the right
|
|
74
|
+
helper.stub!(:sync_options).and_return({:direction => :right})
|
|
75
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
76
|
+
syncer.source.should == :left
|
|
77
|
+
syncer.target.should == :right
|
|
78
|
+
syncer.source_record_index.should == 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "default_option should return the correct default options" do
|
|
82
|
+
Syncers::OneWaySyncer.default_options.should == {
|
|
83
|
+
:direction => :right,
|
|
84
|
+
:delete => false, :update => true, :insert => true
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "sync_difference should only delete if :delete option is given" do
|
|
89
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
90
|
+
helper = SyncHelper.new(sync)
|
|
91
|
+
helper.stub!(:sync_options).and_return({:direction => :left})
|
|
92
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
93
|
+
helper.should_not_receive(:delete_record)
|
|
94
|
+
helper.should_not_receive(:update_record)
|
|
95
|
+
helper.should_not_receive(:insert_record)
|
|
96
|
+
syncer.sync_difference(:left, :dummy_record)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "sync_difference should delete in the right database" do
|
|
100
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
101
|
+
helper = SyncHelper.new(sync)
|
|
102
|
+
helper.stub!(:sync_options).and_return({:direction => :left, :delete => true})
|
|
103
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
104
|
+
helper.should_receive(:delete_record).with(:left, :dummy_record)
|
|
105
|
+
helper.should_not_receive(:update_record)
|
|
106
|
+
helper.should_not_receive(:insert_record)
|
|
107
|
+
syncer.sync_difference(:left, :dummy_record)
|
|
108
|
+
|
|
109
|
+
helper = SyncHelper.new(sync)
|
|
110
|
+
helper.stub!(:sync_options).and_return({:direction => :right, :delete => true})
|
|
111
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
112
|
+
helper.should_receive(:delete_record).with(:right, :dummy_record)
|
|
113
|
+
syncer.sync_difference(:right, :dummy_record)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "sync_difference should not insert if :insert option is not true" do
|
|
117
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
118
|
+
helper = SyncHelper.new(sync)
|
|
119
|
+
helper.stub!(:sync_options).and_return({:direction => :left, :insert => false})
|
|
120
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
121
|
+
helper.should_not_receive(:delete_record)
|
|
122
|
+
helper.should_not_receive(:update_record)
|
|
123
|
+
helper.should_not_receive(:insert_record)
|
|
124
|
+
syncer.sync_difference(:right, :dummy_record)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "sync_difference should insert in the right database" do
|
|
128
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
129
|
+
helper = SyncHelper.new(sync)
|
|
130
|
+
helper.stub!(:sync_options).and_return({:direction => :left, :insert => true})
|
|
131
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
132
|
+
helper.should_not_receive(:delete_record)
|
|
133
|
+
helper.should_not_receive(:update_record)
|
|
134
|
+
helper.should_receive(:insert_record).with(:left, :dummy_record)
|
|
135
|
+
syncer.sync_difference(:right, :dummy_record)
|
|
136
|
+
|
|
137
|
+
helper = SyncHelper.new(sync)
|
|
138
|
+
helper.stub!(:sync_options).and_return({:direction => :right, :insert => true})
|
|
139
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
140
|
+
helper.should_receive(:insert_record).with(:right, :dummy_record)
|
|
141
|
+
syncer.sync_difference(:left, :dummy_record)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "sync_difference should not update if :update option is not true" do
|
|
145
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
146
|
+
helper = SyncHelper.new(sync)
|
|
147
|
+
helper.stub!(:sync_options).and_return({:direction => :left, :update => false})
|
|
148
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
149
|
+
helper.should_not_receive(:delete_record)
|
|
150
|
+
helper.should_not_receive(:update_record)
|
|
151
|
+
helper.should_not_receive(:insert_record)
|
|
152
|
+
syncer.sync_difference(:conflict, :dummy_records)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "sync_difference should update the right values in the right database" do
|
|
156
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
157
|
+
helper = SyncHelper.new(sync)
|
|
158
|
+
helper.stub!(:sync_options).and_return({:direction => :left, :update => true})
|
|
159
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
160
|
+
helper.should_not_receive(:delete_record)
|
|
161
|
+
helper.should_receive(:update_record).with(:left, :right_record)
|
|
162
|
+
helper.should_not_receive(:insert_record)
|
|
163
|
+
syncer.sync_difference(:conflict, [:left_record, :right_record])
|
|
164
|
+
|
|
165
|
+
helper = SyncHelper.new(sync)
|
|
166
|
+
helper.stub!(:sync_options).and_return({:direction => :right, :update => true})
|
|
167
|
+
syncer = Syncers::OneWaySyncer.new(helper)
|
|
168
|
+
helper.should_receive(:update_record).with(:right, :left_record)
|
|
169
|
+
syncer.sync_difference(:conflict, [:left_record, :right_record])
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableScanHelper do
|
|
6
|
+
before(:each) do
|
|
7
|
+
@scan = Object.new
|
|
8
|
+
@scan.extend TableScanHelper
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "rank_rows should calculate the correct rank of rows based on their primary keys" do
|
|
12
|
+
@scan.stub!(:primary_key_names).and_return(['first_id', 'second_id'])
|
|
13
|
+
@scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 0
|
|
14
|
+
@scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 2}).should == -1
|
|
15
|
+
@scan.rank_rows({'first_id' => 2, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 1
|
|
16
|
+
lambda {@scan.rank_rows(nil,nil)}.should raise_error(RuntimeError, 'At least one of left_row and right_row must not be nil!')
|
|
17
|
+
@scan.rank_rows(nil, {'first_id' => 1, 'second_id' => 1}).should == 1
|
|
18
|
+
@scan.rank_rows({'first_id' => 1, 'second_id' => 1}, nil).should == -1
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "table_scan_class should return TableScan for non-proxied sessions" do
|
|
22
|
+
TableScanHelper.scan_class(Session.new(standard_config)).should == DirectTableScan
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "table_scan_class should return ProxiedTableScan for proxied sessions" do
|
|
26
|
+
ensure_proxy
|
|
27
|
+
TableScanHelper.scan_class(Session.new(proxied_config)).should == ProxiedTableScan
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableScan do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should raise exception if table doesn't have primary keys" do
|
|
11
|
+
session = Session.new
|
|
12
|
+
lambda {TableScan.new session, 'extender_without_key'} \
|
|
13
|
+
.should raise_error(RuntimeError, /.*extender_without_key.*primary key/)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "initialize should cache the primary keys of the given table" do
|
|
17
|
+
session = Session.new
|
|
18
|
+
scann = TableScan.new session, 'scanner_records'
|
|
19
|
+
scann.primary_key_names.should == ['id']
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "initialize should use the name of the left table as overwritable default for right table" do
|
|
23
|
+
session = Session.new
|
|
24
|
+
TableScan.new(session, 'scanner_records').right_table.should == 'scanner_records'
|
|
25
|
+
TableScan.new(session, 'scanner_records', 'dummy').right_table.should == 'dummy'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "progress_printer= should store the progress printer class" do
|
|
29
|
+
session = Session.new
|
|
30
|
+
TableScan.new(session, 'scanner_records').progress_printer = :dummy_printer_class
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "update_progres should create the progress printer and report the progress" do
|
|
34
|
+
session = Session.new
|
|
35
|
+
scan = TableScan.new(session, 'extender_one_record', 'extender_no_record')
|
|
36
|
+
|
|
37
|
+
progress_printer_class = mock("progress printer class")
|
|
38
|
+
progress_printer = mock("progress printer")
|
|
39
|
+
progress_printer_class.should_receive(:new).
|
|
40
|
+
with(1, scan.session, 'extender_one_record', 'extender_no_record').
|
|
41
|
+
and_return(progress_printer)
|
|
42
|
+
progress_printer.should_receive(:step).with(2)
|
|
43
|
+
progress_printer.should_receive(:step).with(3)
|
|
44
|
+
|
|
45
|
+
scan.progress_printer = progress_printer_class
|
|
46
|
+
scan.update_progress 2
|
|
47
|
+
scan.update_progress 3
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableSorter do
|
|
6
|
+
before(:each) do
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "sort should order the tables correctly" do
|
|
10
|
+
tables = [
|
|
11
|
+
'scanner_records',
|
|
12
|
+
'referencing_table',
|
|
13
|
+
'referenced_table',
|
|
14
|
+
'scanner_text_key',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
sorted_tables = [
|
|
18
|
+
'scanner_records',
|
|
19
|
+
'referenced_table',
|
|
20
|
+
'referencing_table',
|
|
21
|
+
'scanner_text_key',
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
sorter = TableSorter.new Session.new(standard_config), tables
|
|
25
|
+
sorter.sort.should == sorted_tables
|
|
26
|
+
|
|
27
|
+
# verify that we are using TSort#tsort to get that result
|
|
28
|
+
sorter.should_not_receive(:tsort)
|
|
29
|
+
sorter.sort
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableSpecResolver do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
@session = Session.new
|
|
9
|
+
@resolver = TableSpecResolver.new @session
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "initialize should store the session and cache the tables of the session" do
|
|
13
|
+
@resolver.session.should == @session
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "tables should return the tables of the specified database" do
|
|
17
|
+
@resolver.tables(:left).should == @session.left.tables
|
|
18
|
+
@resolver.tables(:right).should == @session.right.tables
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "resolve should resolve direct table names correctly" do
|
|
22
|
+
@resolver.resolve(['scanner_records', 'referenced_table']).should == [
|
|
23
|
+
{:left => 'scanner_records', :right => 'scanner_records'},
|
|
24
|
+
{:left => 'referenced_table', :right => 'referenced_table'}
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "resolve should resolve table name pairs correctly" do
|
|
29
|
+
@resolver.resolve(['left_table , right_table']).should == [
|
|
30
|
+
{:left => 'left_table', :right => 'right_table'}
|
|
31
|
+
]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "resolve should complain about non-existing tables" do
|
|
35
|
+
lambda {@resolver.resolve(['dummy, scanner_records'])}.
|
|
36
|
+
should raise_error(/non-existing.*dummy/)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "resolve should not check for non-existing tables if that is disabled" do
|
|
40
|
+
lambda {@resolver.resolve(['dummy, scanner_records'], [], false)}.
|
|
41
|
+
should_not raise_error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "resolve should resolve string in form of regular expression correctly" do
|
|
45
|
+
@resolver.resolve(['/SCANNER_RECORDS|scanner_text_key/']).sort { |a,b|
|
|
46
|
+
a[:left] <=> b[:left]
|
|
47
|
+
}.should == [
|
|
48
|
+
{:left => 'scanner_records', :right => 'scanner_records'},
|
|
49
|
+
{:left => 'scanner_text_key', :right => 'scanner_text_key'}
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "resolve should resolve regular expressions correctly" do
|
|
54
|
+
@resolver.resolve([/SCANNER_RECORDS|scanner_text_key/]).sort { |a,b|
|
|
55
|
+
a[:left] <=> b[:left]
|
|
56
|
+
}.should == [
|
|
57
|
+
{:left => 'scanner_records', :right => 'scanner_records'},
|
|
58
|
+
{:left => 'scanner_text_key', :right => 'scanner_text_key'}
|
|
59
|
+
]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "resolve should should not return the same table multiple times" do
|
|
63
|
+
@resolver.resolve([
|
|
64
|
+
'scanner_records',
|
|
65
|
+
'scanner_records',
|
|
66
|
+
'scanner_records, bla',
|
|
67
|
+
'/scanner_records/'
|
|
68
|
+
]
|
|
69
|
+
).should == [
|
|
70
|
+
{:left => 'scanner_records', :right => 'scanner_records'}
|
|
71
|
+
]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "resolve should not return tables that are excluded" do
|
|
75
|
+
@resolver.resolve(
|
|
76
|
+
[/SCANNER_RECORDS|scanner_text_key/],
|
|
77
|
+
[/scanner_text/]
|
|
78
|
+
).should == [
|
|
79
|
+
{:left => 'scanner_records', :right => 'scanner_records'},
|
|
80
|
+
]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "non_existing_tables should return an empty hash if all tables exist" do
|
|
84
|
+
table_pairs = [{:left => 'scanner_records', :right => 'referenced_table'}]
|
|
85
|
+
@resolver.non_existing_tables(table_pairs).should == {}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "non_existing_tables should return an empty hash if all tables exist" do
|
|
89
|
+
table_pairs = [{:left => 'scanner_records', :right => 'bla'}]
|
|
90
|
+
@resolver.non_existing_tables(table_pairs).should == {:right => ['bla']}
|
|
91
|
+
|
|
92
|
+
table_pairs = [
|
|
93
|
+
{:left => 'blub', :right => 'bla'},
|
|
94
|
+
{:left => 'scanner_records', :right => 'xyz'}
|
|
95
|
+
]
|
|
96
|
+
@resolver.non_existing_tables(table_pairs).should == {
|
|
97
|
+
:left => ['blub'],
|
|
98
|
+
:right => ['bla', 'xyz']
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TableSync do
|
|
6
|
+
it "sync_options should return the correct table specific sync options" do
|
|
7
|
+
config = deep_copy(standard_config)
|
|
8
|
+
old_table_specific_options = config.tables_with_options
|
|
9
|
+
begin
|
|
10
|
+
config.options = {:syncer => :bla}
|
|
11
|
+
config.include_tables 'scanner_records', {:syncer => :blub}
|
|
12
|
+
TableSync.new(Session.new(config), 'scanner_records').sync_options[:syncer] \
|
|
13
|
+
.should == :blub
|
|
14
|
+
ensure
|
|
15
|
+
config.instance_eval {@tables_with_options = old_table_specific_options}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "execute_sync_hook should work if the hook is not configured" do
|
|
20
|
+
session = Session.new standard_config
|
|
21
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
22
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "execute_sync_look should execute the given SQL command" do
|
|
26
|
+
config = deep_copy(standard_config)
|
|
27
|
+
config.add_table_options 'scanner_records', :before_table_sync => 'dummy_command'
|
|
28
|
+
session = Session.new config
|
|
29
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
30
|
+
|
|
31
|
+
session.left.should_receive(:execute).with('dummy_command')
|
|
32
|
+
session.right.should_receive(:execute).with('dummy_command')
|
|
33
|
+
|
|
34
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "execute_sync_look should execute the given Proc" do
|
|
38
|
+
config = deep_copy(standard_config)
|
|
39
|
+
received_handler = nil
|
|
40
|
+
config.add_table_options 'scanner_records',
|
|
41
|
+
:before_table_sync => lambda {|helper| received_handler = helper}
|
|
42
|
+
session = Session.new config
|
|
43
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
44
|
+
sync.helper = :dummy_helper
|
|
45
|
+
|
|
46
|
+
sync.execute_sync_hook(:before_table_sync)
|
|
47
|
+
|
|
48
|
+
received_handler.should == :dummy_helper
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "run should synchronize the databases" do
|
|
52
|
+
config = deep_copy(standard_config)
|
|
53
|
+
config.options[:committer] = :never_commit
|
|
54
|
+
config.options[:logged_sync_events] = [:all_conflicts]
|
|
55
|
+
before_hook_called = false
|
|
56
|
+
after_hook_called = false
|
|
57
|
+
config.options[:before_table_sync] = lambda {|helper| before_hook_called = true}
|
|
58
|
+
config.options[:after_table_sync] = lambda { |helper| after_hook_called = true}
|
|
59
|
+
session = Session.new(config)
|
|
60
|
+
begin
|
|
61
|
+
sync = TableSync.new(session, 'scanner_records')
|
|
62
|
+
sync.run
|
|
63
|
+
|
|
64
|
+
# Verify that sync events are logged
|
|
65
|
+
row = session.left.select_one("select * from rr_logged_events where change_key = '2' order by id")
|
|
66
|
+
row['change_table'].should == 'scanner_records'
|
|
67
|
+
row['diff_type'].should == 'conflict'
|
|
68
|
+
row['description'].should == 'left_wins'
|
|
69
|
+
|
|
70
|
+
# verify that the table was synchronized
|
|
71
|
+
left_records = session.left.select_all("select * from scanner_records order by id")
|
|
72
|
+
right_records = session.right.select_all("select * from scanner_records order by id")
|
|
73
|
+
left_records.should == right_records
|
|
74
|
+
|
|
75
|
+
# verify that hooks where called
|
|
76
|
+
before_hook_called.should be_true
|
|
77
|
+
after_hook_called.should be_true
|
|
78
|
+
ensure
|
|
79
|
+
Committers::NeverCommitter.rollback_current_session
|
|
80
|
+
session.left.execute "delete from rr_logged_events"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe TriggerModeSwitcher do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should save the session and initialize triggers hash" do
|
|
11
|
+
session = Session.new
|
|
12
|
+
switcher = TriggerModeSwitcher.new(session)
|
|
13
|
+
switcher.session.should == session
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "exclude_rr_activity should switch the trigger for the given table" do
|
|
17
|
+
switcher = TriggerModeSwitcher.new(Session.new)
|
|
18
|
+
|
|
19
|
+
switcher.should_receive(:switch_trigger_mode).with(:right, 'right1', true).once
|
|
20
|
+
switcher.exclude_rr_activity(:right, 'right1')
|
|
21
|
+
|
|
22
|
+
# Verify that for a given table, the trigger is not modified multiple times
|
|
23
|
+
switcher.exclude_rr_activity(:right, 'right1')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "restore_triggers should restore the triggers" do
|
|
27
|
+
switcher = TriggerModeSwitcher.new(Session.new)
|
|
28
|
+
|
|
29
|
+
switcher.stub!(:switch_trigger_mode)
|
|
30
|
+
switcher.exclude_rr_activity :left, 'left1'
|
|
31
|
+
switcher.should_receive(:switch_trigger_mode).with(:left, 'left1', false).once
|
|
32
|
+
switcher.restore_triggers
|
|
33
|
+
switcher.restore_triggers # ensure the restore is only done once
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "switch_trigger_mode should switch the exclude_rr_activity mode as specified" do
|
|
37
|
+
session = nil
|
|
38
|
+
initializer = nil
|
|
39
|
+
begin
|
|
40
|
+
session = Session.new
|
|
41
|
+
initializer = ReplicationInitializer.new(session)
|
|
42
|
+
initializer.create_trigger(:left, 'trigger_test')
|
|
43
|
+
|
|
44
|
+
switcher = TriggerModeSwitcher.new session
|
|
45
|
+
switcher.switch_trigger_mode :left, 'trigger_test', true
|
|
46
|
+
|
|
47
|
+
session.left.insert_record 'trigger_test', {
|
|
48
|
+
'first_id' => 1,
|
|
49
|
+
'second_id' => 2,
|
|
50
|
+
'name' => 'blub'
|
|
51
|
+
}
|
|
52
|
+
session.left.execute "insert into rr_running_flags values(1)"
|
|
53
|
+
session.left.insert_record 'trigger_test', {
|
|
54
|
+
'first_id' => 1,
|
|
55
|
+
'second_id' => 3,
|
|
56
|
+
'name' => 'bla'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
rows = session.left.select_all("select * from rr_pending_changes order by id")
|
|
60
|
+
rows.each {|row| row.delete 'id'; row.delete 'change_time'}
|
|
61
|
+
rows.should == [{
|
|
62
|
+
'change_table' => 'trigger_test',
|
|
63
|
+
'change_key' => 'first_id|1|second_id|2',
|
|
64
|
+
'change_new_key' => nil,
|
|
65
|
+
'change_type' => 'I'
|
|
66
|
+
}]
|
|
67
|
+
ensure
|
|
68
|
+
initializer.drop_trigger :left, 'trigger_test' if initializer
|
|
69
|
+
if session
|
|
70
|
+
session.left.execute 'delete from rr_running_flags'
|
|
71
|
+
session.left.execute 'delete from trigger_test'
|
|
72
|
+
session.left.execute 'delete from rr_pending_changes'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "switch_trigger_mode should not switch the trigger mode if the table has no trigger" do
|
|
78
|
+
session = Session.new
|
|
79
|
+
switcher = TriggerModeSwitcher.new session
|
|
80
|
+
session.left.should_not_receive(:execute)
|
|
81
|
+
switcher.switch_trigger_mode(:left, 'scanner_records', true)
|
|
82
|
+
end
|
|
83
|
+
end
|