rubyrep 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +137 -0
  4. data/README.txt +37 -0
  5. data/Rakefile +30 -0
  6. data/bin/rubyrep +8 -0
  7. data/config/hoe.rb +72 -0
  8. data/config/mysql_config.rb +25 -0
  9. data/config/postgres_config.rb +21 -0
  10. data/config/proxied_test_config.rb +14 -0
  11. data/config/redmine_config.rb +17 -0
  12. data/config/rep_config.rb +20 -0
  13. data/config/requirements.rb +32 -0
  14. data/config/test_config.rb +20 -0
  15. data/lib/rubyrep/base_runner.rb +195 -0
  16. data/lib/rubyrep/command_runner.rb +144 -0
  17. data/lib/rubyrep/committers/buffered_committer.rb +140 -0
  18. data/lib/rubyrep/committers/committers.rb +146 -0
  19. data/lib/rubyrep/configuration.rb +240 -0
  20. data/lib/rubyrep/connection_extenders/connection_extenders.rb +133 -0
  21. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +284 -0
  22. data/lib/rubyrep/connection_extenders/mysql_extender.rb +168 -0
  23. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +261 -0
  24. data/lib/rubyrep/database_proxy.rb +52 -0
  25. data/lib/rubyrep/direct_table_scan.rb +75 -0
  26. data/lib/rubyrep/generate_runner.rb +105 -0
  27. data/lib/rubyrep/initializer.rb +39 -0
  28. data/lib/rubyrep/logged_change.rb +326 -0
  29. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  30. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  31. data/lib/rubyrep/proxy_connection.rb +318 -0
  32. data/lib/rubyrep/proxy_cursor.rb +44 -0
  33. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  34. data/lib/rubyrep/proxy_runner.rb +89 -0
  35. data/lib/rubyrep/replication_difference.rb +91 -0
  36. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  37. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +204 -0
  38. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  39. data/lib/rubyrep/replication_helper.rb +104 -0
  40. data/lib/rubyrep/replication_initializer.rb +307 -0
  41. data/lib/rubyrep/replication_run.rb +48 -0
  42. data/lib/rubyrep/replication_runner.rb +138 -0
  43. data/lib/rubyrep/replicators/replicators.rb +37 -0
  44. data/lib/rubyrep/replicators/two_way_replicator.rb +334 -0
  45. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  46. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  47. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  48. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  49. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  50. data/lib/rubyrep/scan_runner.rb +25 -0
  51. data/lib/rubyrep/session.rb +177 -0
  52. data/lib/rubyrep/sync_helper.rb +111 -0
  53. data/lib/rubyrep/sync_runner.rb +31 -0
  54. data/lib/rubyrep/syncers/syncers.rb +112 -0
  55. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  56. data/lib/rubyrep/table_scan.rb +54 -0
  57. data/lib/rubyrep/table_scan_helper.rb +38 -0
  58. data/lib/rubyrep/table_sorter.rb +70 -0
  59. data/lib/rubyrep/table_spec_resolver.rb +136 -0
  60. data/lib/rubyrep/table_sync.rb +68 -0
  61. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  62. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  63. data/lib/rubyrep/uninstall_runner.rb +92 -0
  64. data/lib/rubyrep/version.rb +9 -0
  65. data/lib/rubyrep.rb +68 -0
  66. data/script/destroy +14 -0
  67. data/script/generate +14 -0
  68. data/script/txt2html +74 -0
  69. data/setup.rb +1585 -0
  70. data/sims/performance/big_rep_spec.rb +100 -0
  71. data/sims/performance/big_scan_spec.rb +57 -0
  72. data/sims/performance/big_sync_spec.rb +141 -0
  73. data/sims/performance/performance.rake +228 -0
  74. data/sims/sim_helper.rb +24 -0
  75. data/spec/base_runner_spec.rb +218 -0
  76. data/spec/buffered_committer_spec.rb +271 -0
  77. data/spec/command_runner_spec.rb +145 -0
  78. data/spec/committers_spec.rb +174 -0
  79. data/spec/configuration_spec.rb +198 -0
  80. data/spec/connection_extender_interface_spec.rb +138 -0
  81. data/spec/connection_extenders_registration_spec.rb +129 -0
  82. data/spec/database_proxy_spec.rb +48 -0
  83. data/spec/database_rake_spec.rb +40 -0
  84. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  85. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  86. data/spec/direct_table_scan_spec.rb +61 -0
  87. data/spec/generate_runner_spec.rb +84 -0
  88. data/spec/initializer_spec.rb +46 -0
  89. data/spec/logged_change_spec.rb +480 -0
  90. data/spec/postgresql_replication_spec.rb +48 -0
  91. data/spec/postgresql_support_spec.rb +57 -0
  92. data/spec/progress_bar_spec.rb +77 -0
  93. data/spec/proxied_table_scan_spec.rb +151 -0
  94. data/spec/proxy_block_cursor_spec.rb +197 -0
  95. data/spec/proxy_connection_spec.rb +399 -0
  96. data/spec/proxy_cursor_spec.rb +56 -0
  97. data/spec/proxy_row_cursor_spec.rb +66 -0
  98. data/spec/proxy_runner_spec.rb +70 -0
  99. data/spec/replication_difference_spec.rb +160 -0
  100. data/spec/replication_extender_interface_spec.rb +365 -0
  101. data/spec/replication_extenders_spec.rb +32 -0
  102. data/spec/replication_helper_spec.rb +121 -0
  103. data/spec/replication_initializer_spec.rb +477 -0
  104. data/spec/replication_run_spec.rb +166 -0
  105. data/spec/replication_runner_spec.rb +213 -0
  106. data/spec/replicators_spec.rb +31 -0
  107. data/spec/rubyrep_spec.rb +8 -0
  108. data/spec/scan_detail_reporter_spec.rb +119 -0
  109. data/spec/scan_progress_printers_spec.rb +68 -0
  110. data/spec/scan_report_printers_spec.rb +67 -0
  111. data/spec/scan_runner_spec.rb +50 -0
  112. data/spec/scan_summary_reporter_spec.rb +61 -0
  113. data/spec/session_spec.rb +212 -0
  114. data/spec/spec.opts +1 -0
  115. data/spec/spec_helper.rb +295 -0
  116. data/spec/sync_helper_spec.rb +157 -0
  117. data/spec/sync_runner_spec.rb +78 -0
  118. data/spec/syncers_spec.rb +171 -0
  119. data/spec/table_scan_helper_spec.rb +29 -0
  120. data/spec/table_scan_spec.rb +49 -0
  121. data/spec/table_sorter_spec.rb +31 -0
  122. data/spec/table_spec_resolver_spec.rb +102 -0
  123. data/spec/table_sync_spec.rb +84 -0
  124. data/spec/trigger_mode_switcher_spec.rb +83 -0
  125. data/spec/two_way_replicator_spec.rb +551 -0
  126. data/spec/two_way_syncer_spec.rb +256 -0
  127. data/spec/type_casting_cursor_spec.rb +50 -0
  128. data/spec/uninstall_runner_spec.rb +86 -0
  129. data/tasks/database.rake +439 -0
  130. data/tasks/deployment.rake +29 -0
  131. data/tasks/environment.rake +9 -0
  132. data/tasks/java.rake +37 -0
  133. data/tasks/redmine_test.rake +47 -0
  134. data/tasks/rspec.rake +68 -0
  135. data/tasks/rubyrep.tailor +18 -0
  136. data/tasks/stats.rake +19 -0
  137. data/tasks/task_helper.rb +20 -0
  138. data.tar.gz.sig +0 -0
  139. metadata +243 -0
  140. metadata.gz.sig +0 -0
@@ -0,0 +1,160 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ReplicationDifference do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should store the session" do
11
+ session = Session.new
12
+ diff = ReplicationDifference.new session
13
+ diff.session.should == session
14
+ end
15
+
16
+ it "loaded? should return true if a difference was loaded" do
17
+ diff = ReplicationDifference.new Session.new
18
+ diff.should_not be_loaded
19
+ diff.loaded = true
20
+ diff.should be_loaded
21
+ end
22
+
23
+ it "load should leave the instance unloaded if no changes are available" do
24
+ diff = ReplicationDifference.new Session.new
25
+ diff.load
26
+ diff.should_not be_loaded
27
+ end
28
+
29
+ it "load should load left differences successfully" do
30
+ session = Session.new
31
+ session.left.begin_db_transaction
32
+ begin
33
+ session.left.insert_record 'rr_pending_changes', {
34
+ 'change_table' => 'scanner_records',
35
+ 'change_key' => 'id|1',
36
+ 'change_type' => 'I',
37
+ 'change_time' => Time.now
38
+ }
39
+ diff = ReplicationDifference.new session
40
+ diff.load
41
+
42
+ diff.should be_loaded
43
+ diff.type.should == :left
44
+ diff.changes[:left].key.should == {'id' => '1'}
45
+ ensure
46
+ session.left.rollback_db_transaction
47
+ end
48
+ end
49
+
50
+ it "load should load right differences successfully" do
51
+ session = Session.new
52
+ session.right.begin_db_transaction
53
+ begin
54
+ session.right.insert_record 'rr_pending_changes', {
55
+ 'change_table' => 'scanner_records',
56
+ 'change_key' => 'id|1',
57
+ 'change_type' => 'D',
58
+ 'change_time' => Time.now
59
+ }
60
+ diff = ReplicationDifference.new session
61
+ diff.load
62
+
63
+ diff.should be_loaded
64
+ diff.type.should == :right
65
+ diff.changes[:right].key.should == {'id' => '1'}
66
+ ensure
67
+ session.right.rollback_db_transaction
68
+ end
69
+ end
70
+
71
+ it "load should load conflict differences successfully" do
72
+ config = deep_copy(standard_config)
73
+ config.included_table_specs.clear
74
+ config.include_tables "table_with_manual_key, extender_without_key"
75
+
76
+ session = Session.new config
77
+ session.left.begin_db_transaction
78
+ session.right.begin_db_transaction
79
+ begin
80
+ session.left.insert_record 'rr_pending_changes', {
81
+ 'change_table' => 'dummy_table',
82
+ 'change_key' => 'id|2',
83
+ 'change_type' => 'I',
84
+ 'change_time' => Time.now
85
+ }
86
+ session.left.insert_record 'rr_pending_changes', {
87
+ 'change_table' => 'table_with_manual_key',
88
+ 'change_key' => 'id|1',
89
+ 'change_new_key' => 'id|1',
90
+ 'change_type' => 'U',
91
+ 'change_time' => 5.seconds.from_now
92
+ }
93
+ session.right.insert_record 'rr_pending_changes', {
94
+ 'change_table' => 'extender_without_key',
95
+ 'change_key' => 'id|1',
96
+ 'change_type' => 'D',
97
+ 'change_time' => 5.seconds.ago
98
+ }
99
+ diff = ReplicationDifference.new session
100
+ diff.load
101
+
102
+ diff.should be_loaded
103
+ diff.type.should == :conflict
104
+ diff.changes[:left].type.should == :update
105
+ diff.changes[:left].table.should == 'table_with_manual_key'
106
+ diff.changes[:left].key.should == {'id' => '1'}
107
+ diff.changes[:right].type.should == :delete
108
+ diff.changes[:right].table.should == 'extender_without_key'
109
+ diff.changes[:right].key.should == {'id' => '1'}
110
+ ensure
111
+ session.left.rollback_db_transaction
112
+ session.right.rollback_db_transaction
113
+ end
114
+ end
115
+
116
+ it "amend should amend the replication difference with new found changes" do
117
+ session = Session.new
118
+ session.left.begin_db_transaction
119
+ session.right.begin_db_transaction
120
+ begin
121
+ session.right.insert_record 'rr_pending_changes', {
122
+ 'change_table' => 'scanner_records',
123
+ 'change_key' => 'id|1',
124
+ 'change_type' => 'I',
125
+ 'change_time' => Time.now
126
+ }
127
+ diff = ReplicationDifference.new session
128
+ diff.load
129
+
130
+ diff.should be_loaded
131
+ diff.type.should == :right
132
+ diff.changes[:right].key.should == {'id' => '1'}
133
+
134
+ # if there are no changes, the diff should still be the same
135
+ diff.amend
136
+ diff.type.should == :right
137
+ diff.changes[:right].key.should == {'id' => '1'}
138
+
139
+ # should recognize new changes
140
+ session.left.insert_record 'rr_pending_changes', {
141
+ 'change_table' => 'scanner_records',
142
+ 'change_key' => 'id|1',
143
+ 'change_type' => 'D',
144
+ 'change_time' => Time.now
145
+ }
146
+ diff.amend
147
+ diff.type.should == :conflict
148
+ diff.changes[:left].key.should == {'id' => '1'}
149
+ diff.changes[:right].key.should == {'id' => '1'}
150
+ ensure
151
+ session.right.rollback_db_transaction
152
+ session.left.rollback_db_transaction
153
+ end
154
+ end
155
+
156
+ it "to_yaml should blank out session" do
157
+ diff = ReplicationDifference.new :dummy_session
158
+ diff.to_yaml.should_not =~ /session/
159
+ end
160
+ end
@@ -0,0 +1,365 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require 'yaml'
3
+
4
+ include RR
5
+
6
+ # All ReplicationExtenders need to pass this spec
7
+ describe "ReplicationExtender", :shared => true do
8
+ before(:each) do
9
+ end
10
+
11
+ it "create_replication_trigger created triggers should log data changes" do
12
+ session = nil
13
+ begin
14
+ session = Session.new
15
+ session.left.begin_db_transaction
16
+ params = {
17
+ :trigger_name => 'rr_trigger_test',
18
+ :table => 'trigger_test',
19
+ :keys => ['first_id', 'second_id'],
20
+ :log_table => 'rr_pending_changes',
21
+ :key_sep => '|',
22
+ :exclude_rr_activity => false,
23
+ }
24
+ session.left.create_replication_trigger params
25
+
26
+ change_start = Time.now
27
+
28
+ session.left.insert_record 'trigger_test', {
29
+ 'first_id' => 1,
30
+ 'second_id' => 2,
31
+ 'name' => 'bla'
32
+ }
33
+ session.left.execute "update trigger_test set second_id = 9 where first_id = 1 and second_id = 2"
34
+ session.left.delete_record 'trigger_test', {
35
+ 'first_id' => 1,
36
+ 'second_id' => 9,
37
+ }
38
+
39
+ rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
40
+
41
+ # Verify that the timestamps are created correctly
42
+ rows.each do |row|
43
+ Time.parse(row['change_time']).to_i >= change_start.to_i
44
+ Time.parse(row['change_time']).to_i <= Time.now.to_i
45
+ end
46
+
47
+ rows.each {|row| row.delete 'id'; row.delete 'change_time'}
48
+ rows.should == [
49
+ {'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|2', 'change_new_key' => nil, 'change_type' => 'I'},
50
+ {'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|2', 'change_new_key' => 'first_id|1|second_id|9', 'change_type' => 'U'},
51
+ {'change_table' => 'trigger_test', 'change_key' => 'first_id|1|second_id|9', 'change_new_key' => nil, 'change_type' => 'D'},
52
+ ]
53
+ ensure
54
+ session.left.execute 'delete from trigger_test' if session
55
+ session.left.execute 'delete from rr_pending_changes' if session
56
+ session.left.rollback_db_transaction if session
57
+ end
58
+ end
59
+
60
+ it "created triggers should not log rubyrep initiated changes if :exclude_rubyrep_activity is true" do
61
+ session = nil
62
+ begin
63
+ session = Session.new
64
+ session.left.begin_db_transaction
65
+ params = {
66
+ :trigger_name => 'rr_trigger_test',
67
+ :table => 'trigger_test',
68
+ :keys => ['first_id', 'second_id'],
69
+ :log_table => 'rr_pending_changes',
70
+ :key_sep => '|',
71
+ :exclude_rr_activity => true,
72
+ :activity_table => "rr_running_flags",
73
+ }
74
+ session.left.create_replication_trigger params
75
+
76
+ session.left.insert_record 'rr_running_flags', {
77
+ 'active' => 1
78
+ }
79
+ session.left.insert_record 'trigger_test', {
80
+ 'first_id' => 1,
81
+ 'second_id' => 2,
82
+ 'name' => 'bla'
83
+ }
84
+ session.left.connection.execute('delete from rr_running_flags')
85
+ session.left.insert_record 'trigger_test', {
86
+ 'first_id' => 1,
87
+ 'second_id' => 3,
88
+ 'name' => 'bla'
89
+ }
90
+
91
+ rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
92
+ rows.each {|row| row.delete 'id'; row.delete 'change_time'}
93
+ rows.should == [{
94
+ 'change_table' => 'trigger_test',
95
+ 'change_key' => 'first_id|1|second_id|3',
96
+ 'change_new_key' => nil,
97
+ 'change_type' => 'I'
98
+ }]
99
+ ensure
100
+ session.left.execute 'delete from trigger_test' if session
101
+ session.left.execute 'delete from rr_pending_changes' if session
102
+ session.left.rollback_db_transaction if session
103
+ end
104
+ end
105
+
106
+ it "created triggers should work with tables having non-combined primary keys" do
107
+ session = nil
108
+ begin
109
+ session = Session.new
110
+ session.left.begin_db_transaction
111
+ params = {
112
+ :trigger_name => 'rr_extender_no_record',
113
+ :table => 'extender_no_record',
114
+ :keys => ['id'],
115
+ :log_table => 'rr_pending_changes',
116
+ :key_sep => '|',
117
+ }
118
+ session.left.create_replication_trigger params
119
+ session.left.insert_record 'extender_no_record', {
120
+ 'id' => 9,
121
+ 'name' => 'bla'
122
+ }
123
+ rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
124
+ rows.each {|row| row.delete 'id'; row.delete 'change_time'}
125
+ rows.should == [{
126
+ 'change_table' => 'extender_no_record',
127
+ 'change_key' => 'id|9',
128
+ 'change_new_key' => nil,
129
+ 'change_type' => 'I'
130
+ }]
131
+ ensure
132
+ if session
133
+ session.left.execute 'delete from extender_no_record'
134
+ session.left.execute 'delete from rr_pending_changes'
135
+ session.left.drop_replication_trigger('rr_extender_no_record', 'extender_no_record')
136
+ session.left.rollback_db_transaction
137
+ end
138
+ end
139
+ end
140
+
141
+ it "created triggers should work with primary keys holding multi-byte text values" do
142
+ session = nil
143
+ begin
144
+ session = Session.new
145
+ session.left.begin_db_transaction
146
+ params = {
147
+ :trigger_name => 'rr_scanner_text_key',
148
+ :table => 'scanner_text_key',
149
+ :keys => ['text_id'],
150
+ :log_table => 'rr_pending_changes',
151
+ :key_sep => '|',
152
+ }
153
+ session.left.create_replication_trigger params
154
+ session.left.insert_record 'scanner_text_key', {
155
+ 'text_id' => 'よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.',
156
+ 'name' => 'bla'
157
+ }
158
+ rows = session.left.connection.select_all("select * from rr_pending_changes order by id")
159
+ rows.each {|row| row.delete 'id'; row.delete 'change_time'}
160
+ rows.should == [{
161
+ 'change_table' => 'scanner_text_key',
162
+ 'change_key' => 'text_id|よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.',
163
+ 'change_new_key' => nil,
164
+ 'change_type' => 'I'
165
+ }]
166
+ found_key = rows[0]['change_key'].sub(/^text_id\|/, '')
167
+ session.left.
168
+ select_one("select * from scanner_text_key where text_id = '#{found_key}'").
169
+ should_not be_nil
170
+ ensure
171
+ if session
172
+ session.left.execute "delete from scanner_text_key where text_id = 'よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.'"
173
+ session.left.execute 'delete from rr_pending_changes'
174
+ session.left.drop_replication_trigger('rr_scanner_text_key', 'scanner_text_key')
175
+ session.left.rollback_db_transaction
176
+ end
177
+ end
178
+ end
179
+
180
+ it "replication_trigger_exists? and drop_replication_trigger should work correctly" do
181
+ session = nil
182
+ begin
183
+ session = Session.new
184
+ if session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test')
185
+ session.left.drop_replication_trigger('rr_trigger_test', 'trigger_test')
186
+ end
187
+ session.left.begin_db_transaction
188
+ params = {
189
+ :trigger_name => 'rr_trigger_test',
190
+ :table => 'trigger_test',
191
+ :keys => ['first_id'],
192
+ :log_table => 'rr_pending_changes',
193
+ :key_sep => '|',
194
+ }
195
+ session.left.create_replication_trigger params
196
+
197
+ session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test').
198
+ should be_true
199
+ session.left.drop_replication_trigger('rr_trigger_test', 'trigger_test')
200
+ session.left.replication_trigger_exists?('rr_trigger_test', 'trigger_test').
201
+ should be_false
202
+ ensure
203
+ session.left.rollback_db_transaction if session
204
+ end
205
+ end
206
+
207
+ it "sequence_values should return an empty hash if table has no sequences" do
208
+ session = Session.new
209
+ session.left.sequence_values('rr', 'scanner_text_key').
210
+ should == {}
211
+ end
212
+
213
+ it "sequence_values should return the correct sequence settings" do
214
+ session = nil
215
+ begin
216
+ session = Session.new
217
+ session.left.begin_db_transaction
218
+ session.left.execute 'delete from sequence_test'
219
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
220
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
221
+ session.left.update_sequences \
222
+ 'rr', 'sequence_test', 5, 2,
223
+ left_sequence_values, right_sequence_values, 5
224
+ sequence_value = session.left.sequence_values('rr', 'sequence_test').values[0]
225
+ sequence_value[:increment].should == 5
226
+ (sequence_value[:value] % 5).should == 2
227
+ ensure
228
+ session.left.clear_sequence_setup 'rr', 'sequence_test' if session
229
+ session.left.execute "delete from sequence_test" if session
230
+ session.left.rollback_db_transaction if session
231
+ end
232
+ end
233
+
234
+ it "update_sequences should ensure that a table's auto generated ID values have the correct increment and offset" do
235
+ session = nil
236
+ begin
237
+ session = Session.new
238
+ session.left.begin_db_transaction
239
+
240
+ # Note:
241
+ # Calling ensure_sequence_setup twice with different values to ensure that
242
+ # it is actually does something.
243
+
244
+ session.left.execute 'delete from sequence_test'
245
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
246
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
247
+ session.left.update_sequences \
248
+ 'rr', 'sequence_test', 1, 0,
249
+ left_sequence_values, right_sequence_values, 5
250
+ id1, id2 = get_example_sequence_values(session)
251
+ (id2 - id1).should == 1
252
+
253
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
254
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
255
+ session.left.update_sequences \
256
+ 'rr', 'sequence_test', 5, 2,
257
+ left_sequence_values, right_sequence_values, 5
258
+ id1, id2 = get_example_sequence_values(session)
259
+ (id2 - id1).should == 5
260
+ (id1 % 5).should == 2
261
+ ensure
262
+ session.left.clear_sequence_setup 'rr', 'sequence_test' if session
263
+ session.left.execute "delete from sequence_test" if session
264
+ session.left.rollback_db_transaction if session
265
+ end
266
+ end
267
+
268
+ it "update_sequences shoud set the sequence up correctly if the table is not empty" do
269
+ session = nil
270
+ begin
271
+ session = Session.new
272
+ session.left.begin_db_transaction
273
+ session.left.execute 'delete from sequence_test'
274
+ session.left.insert_record 'sequence_test', { 'name' => 'whatever' }
275
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
276
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
277
+ session.left.update_sequences \
278
+ 'rr', 'sequence_test', 2, 0,
279
+ left_sequence_values, right_sequence_values, 5
280
+ id1, id2 = get_example_sequence_values(session)
281
+ (id2 - id1).should == 2
282
+ ensure
283
+ session.left.clear_sequence_setup 'rr', 'sequence_test' if session
284
+ session.left.execute "delete from sequence_test" if session
285
+ session.left.rollback_db_transaction if session
286
+ end
287
+ end
288
+
289
+ it "update_sequences shoud set the sequence up correctly if the other table is already set up correctly" do
290
+ session = nil
291
+ begin
292
+ session = Session.new
293
+ session.left.begin_db_transaction
294
+ session.right.begin_db_transaction
295
+ session.left.execute 'delete from sequence_test'
296
+ session.left.insert_record 'sequence_test', { 'name' => 'whatever' }
297
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
298
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
299
+
300
+ # setup right table sequence
301
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
302
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
303
+ session.right.update_sequences \
304
+ 'rr', 'sequence_test', 2, 1,
305
+ left_sequence_values, right_sequence_values, 5
306
+
307
+ # now setup left table sequence and verify result
308
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
309
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
310
+ session.left.update_sequences \
311
+ 'rr', 'sequence_test', 2, 0,
312
+ left_sequence_values, right_sequence_values, 5
313
+ id1, id2 = get_example_sequence_values(session)
314
+ (id2 - id1).should == 2
315
+ ensure
316
+ session.left.clear_sequence_setup 'rr', 'sequence_test' if session
317
+ session.right.clear_sequence_setup 'rr', 'sequence_test' if session
318
+ session.left.execute "delete from sequence_test" if session
319
+ session.left.rollback_db_transaction if session
320
+ session.right.rollback_db_transaction if session
321
+ end
322
+ end
323
+
324
+ it "clear_sequence_setup should remove custom sequence settings" do
325
+ session = nil
326
+ begin
327
+ session = Session.new
328
+ session.left.begin_db_transaction
329
+ left_sequence_values = session.left.sequence_values 'rr', 'sequence_test'
330
+ right_sequence_values = session.right.sequence_values 'rr', 'sequence_test'
331
+ session.left.update_sequences \
332
+ 'rr', 'sequence_test', 2, 0,
333
+ left_sequence_values, right_sequence_values, 5
334
+ session.left.clear_sequence_setup 'rr', 'sequence_test'
335
+ id1, id2 = get_example_sequence_values(session)
336
+ (id2 - id1).should == 1
337
+ ensure
338
+ session.left.clear_sequence_setup 'rr', 'sequence_test' if session
339
+ session.left.execute "delete from sequence_test" if session
340
+ session.left.rollback_db_transaction if session
341
+ end
342
+ end
343
+
344
+ it "add_big_primary_key should add a 8 byte, auto incrementing primary key" do
345
+ session = nil
346
+ begin
347
+ session = Session.new
348
+ session.left.drop_table 'big_key_test' if session.left.tables.include? 'big_key_test'
349
+ session.left.create_table 'big_key_test'.to_sym, :id => false do |t|
350
+ t.column :name, :string
351
+ end
352
+ session.left.add_big_primary_key 'big_key_test', 'id'
353
+
354
+ # should auto generate the primary key if not manually specified
355
+ session.left.insert_record 'big_key_test', {'name' => 'bla'}
356
+ session.left.select_one("select id from big_key_test where name = 'bla'")['id'].
357
+ to_i.should > 0
358
+
359
+ # should allow 8 byte values
360
+ session.left.insert_record 'big_key_test', {'id' => 1e18.to_i, 'name' => 'blub'}
361
+ session.left.select_one("select id from big_key_test where name = 'blub'")['id'].
362
+ to_i.should == 1e18.to_i
363
+ end
364
+ end
365
+ end
@@ -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,121 @@
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)
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)
21
+ helper = ReplicationHelper.new(rep_run)
22
+ helper.session.should == rep_run.session
23
+ end
24
+
25
+ it "replication_run should return the current ReplicationRun instance" do
26
+ rep_run = ReplicationRun.new(Session.new)
27
+ helper = ReplicationHelper.new(rep_run)
28
+ helper.replication_run.should == rep_run
29
+ end
30
+
31
+ it "options should return the correct options" do
32
+ session = Session.new
33
+ rep_run = ReplicationRun.new(session)
34
+ helper = ReplicationHelper.new(rep_run)
35
+ helper.options.should == session.configuration.options
36
+ end
37
+
38
+ it "insert_record should insert the given record" do
39
+ rep_run = ReplicationRun.new(Session.new)
40
+ helper = ReplicationHelper.new(rep_run)
41
+ c = helper.instance_eval {committer}
42
+ c.should_receive(:insert_record).with(:right, 'scanner_records', :dummy_record)
43
+ helper.insert_record :right, 'scanner_records', :dummy_record
44
+ end
45
+
46
+ it "update_record should update the given record" do
47
+ rep_run = ReplicationRun.new(Session.new)
48
+ helper = ReplicationHelper.new(rep_run)
49
+ c = helper.instance_eval {committer}
50
+ c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, nil)
51
+ helper.update_record :right, 'scanner_records', :dummy_record
52
+ end
53
+
54
+ it "update_record should update the given record with the provided old key" do
55
+ rep_run = ReplicationRun.new(Session.new)
56
+ helper = ReplicationHelper.new(rep_run)
57
+ c = helper.instance_eval {committer}
58
+ c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, :old_key)
59
+ helper.update_record :right, 'scanner_records', :dummy_record, :old_key
60
+ end
61
+
62
+ it "delete_record should delete the given record" do
63
+ rep_run = ReplicationRun.new(Session.new)
64
+ helper = ReplicationHelper.new(rep_run)
65
+ c = helper.instance_eval {committer}
66
+ c.should_receive(:delete_record).with(:right, 'scanner_records', :dummy_record)
67
+ helper.delete_record :right, 'scanner_records', :dummy_record
68
+ end
69
+
70
+ it "load_record should load the specified record (values converted to original data types)" do
71
+ rep_run = ReplicationRun.new(Session.new)
72
+ helper = ReplicationHelper.new(rep_run)
73
+ helper.load_record(:right, 'scanner_records', 'id' => '2').should == {
74
+ 'id' => 2, # Note: it's a number, not a string...
75
+ 'name' => 'Bob - right database version'
76
+ }
77
+ end
78
+
79
+ it "log_replication_outcome should log the replication outcome correctly" do
80
+ session = Session.new
81
+ session.left.begin_db_transaction
82
+ begin
83
+ rep_run = ReplicationRun.new(session)
84
+ helper = ReplicationHelper.new(rep_run)
85
+ left_change = LoggedChange.new session, :left
86
+ right_change = LoggedChange.new session, :right
87
+ diff = ReplicationDifference.new session
88
+ diff.changes.replace :left => left_change, :right => right_change
89
+ diff.type = :conflict
90
+
91
+ left_change.type, right_change.type = :update, :delete
92
+ left_change.table = right_change.table = 'extender_combined_key'
93
+ left_change.key = right_change.key = {'first_id' => 1, 'second_id' => 2}
94
+
95
+ helper.log_replication_outcome diff, 'ignore', 'ignored'
96
+
97
+ row = session.left.select_one("select * from rr_logged_events order by id desc")
98
+ row['activity'].should == 'replication'
99
+ row['change_table'].should == 'extender_combined_key'
100
+ row['diff_type'].should == 'conflict'
101
+ row['change_key'].should == '"first_id"=>"1", "second_id"=>"2"'
102
+ row['left_change_type'].should == 'update'
103
+ row['right_change_type'].should == 'delete'
104
+ row['description'].should == 'ignore'
105
+ row['long_description'].should == 'ignored'
106
+ Time.parse(row['event_time']).should >= 10.seconds.ago
107
+ row['diff_dump'].should == diff.to_yaml
108
+ ensure
109
+ session.left.rollback_db_transaction if session
110
+ end
111
+ end
112
+
113
+ it "finalize should be delegated to the committer" do
114
+ rep_run = ReplicationRun.new(Session.new)
115
+ helper = ReplicationHelper.new(rep_run)
116
+
117
+ c = helper.instance_eval {@committer}
118
+ c.should_receive(:finalize).with(false)
119
+ helper.finalize(false)
120
+ end
121
+ end