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,100 @@
1
+ require 'benchmark'
2
+
3
+ require File.dirname(__FILE__) + '/../../spec/spec_helper.rb'
4
+ require File.dirname(__FILE__) + '/../sim_helper'
5
+
6
+ include RR
7
+
8
+ describe "Big Rep" do
9
+ before(:each) do
10
+ end
11
+
12
+ # Calculates and returns the number of records of the specified table.
13
+ # * +session+ current Session instance
14
+ # * +database+: designates database, either :left or :right
15
+ # * +table+: name of the table
16
+ def record_quantity(session, database, table = 'big_rep')
17
+ session.send(database).select_one( \
18
+ "select count(id) as count from #{table}")['count'].to_i
19
+ end
20
+
21
+ # Calculates and returns the sum of the number fields of records.
22
+ # * +session+: current Session instance
23
+ # * +database+: designates database, either :left or :right
24
+ def record_sum(session, database)
25
+ session.send(database).select_one( \
26
+ "select sum(number1) + sum(number2) + sum(number3) + sum(number4) as sum
27
+ from big_rep")['sum'].to_f
28
+ end
29
+
30
+ # Returns the number of changes that remain to be replicated
31
+ # * +session+: current Session instance
32
+ def number_changes(session)
33
+ record_quantity(session, :left, 'rr_pending_changes') +
34
+ record_quantity(session, :right, 'rr_pending_changes')
35
+ end
36
+
37
+ # Runs a replication of the big_rep table.
38
+ def run_rep
39
+ config = deep_copy(Initializer.configuration)
40
+ config.options = {
41
+ :committer => :buffered_commit,
42
+ :replication_conflict_handling => :later_wins,
43
+ }
44
+
45
+ session = Session.new config
46
+ initializer = ReplicationInitializer.new session
47
+ begin
48
+ [:left, :right].each do |database|
49
+ session.send(database).execute "insert into big_rep select * from big_rep_backup"
50
+ session.send(database).execute "insert into rr_pending_changes select * from big_rep_pending_changes"
51
+ initializer.create_trigger database, 'big_rep'
52
+ end
53
+
54
+ puts "\nReplicating (#{session.proxied? ? :proxied : :direct}) table big_rep (#{number_changes(session)} changes)"
55
+
56
+ t = Thread.new do
57
+ remaining_changes = number_changes(session)
58
+ progress_bar = ScanProgressPrinters::ProgressBar.new(remaining_changes, session, 'big_rep', 'big_rep')
59
+ while remaining_changes > 0
60
+ sleep 1
61
+ new_remaining_changes = number_changes(session)
62
+ progress_bar.step remaining_changes - new_remaining_changes
63
+ remaining_changes = new_remaining_changes
64
+ end
65
+ end
66
+
67
+ run = ReplicationRun.new session
68
+ benchmark = Benchmark.measure { run.run }
69
+ t.join 10
70
+ puts "\n time required: #{benchmark}"
71
+
72
+ left_fingerprint = {
73
+ :quantity => record_quantity(session, :left),
74
+ :sum => record_sum(session, :left)
75
+ }
76
+ right_fingerprint = {
77
+ :quantity => record_quantity(session, :right),
78
+ :sum => record_sum(session, :right)
79
+ }
80
+ left_fingerprint.should == right_fingerprint
81
+ ensure
82
+ [:left, :right].each do |database|
83
+ initializer.drop_trigger database, 'big_rep'
84
+ session.send(database).execute "delete from big_rep"
85
+ session.send(database).execute "delete from rr_pending_changes"
86
+ end
87
+ end
88
+ end
89
+
90
+ it "Direct Replication should replicate correctly" do
91
+ Initializer.configuration = standard_config
92
+ run_rep
93
+ end
94
+
95
+ it "Proxied Replication should replicate correctly" do
96
+ ensure_proxy
97
+ Initializer.configuration = proxied_config
98
+ run_rep
99
+ end
100
+ end
@@ -0,0 +1,57 @@
1
+ require 'benchmark'
2
+
3
+ require File.dirname(__FILE__) + '/../../spec/spec_helper.rb'
4
+ require File.dirname(__FILE__) + '/../sim_helper'
5
+
6
+ include RR
7
+
8
+ describe "Big Scan" do
9
+ before(:each) do
10
+ end
11
+
12
+ # Runs a scan of the big_scan table
13
+ def run_scan
14
+ session = Session.new
15
+ expected_result = {}
16
+ expected_result[:conflict] = session.left.select_one( \
17
+ "select count(id) as count from big_scan where diff_type = 'conflict'")['count'].to_i
18
+ expected_result[:left] = session.left.select_one( \
19
+ "select count(id) as count from big_scan where diff_type = 'left'")['count'].to_i
20
+ expected_result[:right] = session.right.select_one( \
21
+ "select count(id) as count from big_scan where diff_type = 'right'")['count'].to_i
22
+
23
+ number_records = session.left.select_one( \
24
+ "select count(id) as count from big_scan")['count'].to_i \
25
+ + expected_result[:right]
26
+ number_differences = expected_result.values.inject {|sum, n| sum + n }
27
+ received_result = {:conflict => 0, :left => 0, :right => 0}
28
+
29
+ table_scan_class = TableScanHelper.scan_class(session)
30
+ puts "\nScanning table big_scan (#{number_differences} differences in #{number_records} records) using #{table_scan_class.name}"
31
+
32
+ scan = table_scan_class.new session, 'big_scan'
33
+ scan.progress_printer = RR::ScanProgressPrinters::ProgressBar
34
+ benchmark = Benchmark.measure {
35
+ scan.run do |diff_type, row|
36
+ received_result[diff_type] += 1
37
+ end
38
+ }
39
+ puts "\n time required: #{benchmark}"
40
+
41
+ received_result.should == expected_result
42
+ end
43
+
44
+ it "ProxiedTableScan should identify differences between big_scan tables correctly" do
45
+ Initializer.configuration = deep_copy(proxied_config)
46
+ Initializer.configuration.options[:proxy_block_size] = 100
47
+ ensure_proxy
48
+
49
+ run_scan
50
+ end
51
+
52
+ it "DirectTableScan should identify differences between big_scan tables correctly" do
53
+ Initializer.configuration = standard_config
54
+
55
+ run_scan
56
+ end
57
+ end
@@ -0,0 +1,141 @@
1
+ require 'benchmark'
2
+
3
+ require File.dirname(__FILE__) + '/../../spec/spec_helper.rb'
4
+ require File.dirname(__FILE__) + '/../sim_helper'
5
+
6
+ include RR
7
+
8
+ describe "Big Sync" do
9
+ before(:each) do
10
+ end
11
+
12
+ # Calculates and returns number of records of a given type.
13
+ # * :session: current Session instance
14
+ # * :record_type: type of records (String)
15
+ # * :database: designates database, either :left or :right
16
+ def record_quantity(session, record_type, database)
17
+ session.send(database).select_one( \
18
+ "select count(id) as count from big_scan where diff_type = '#{record_type}'")['count'].to_i
19
+ end
20
+
21
+ # Calculates and returns the sum of the number fields of records of the given type.
22
+ # * :session: current Session instance
23
+ # * :record_type: type of records (:left, :right, :same or :conflict)
24
+ # * :database: designates database, either :left or :right
25
+ def record_sum(session, record_type, database)
26
+ session.send(database).select_one( \
27
+ "select sum(number1) + sum(number2) + sum(number3) + sum(number4) as sum
28
+ from big_scan where diff_type = '#{record_type}'")['sum'].to_f
29
+ end
30
+
31
+ # Runs a sync of the big_scan table.
32
+ # * session: the current Session
33
+ # * expected_result: a hash of record quantities that should result.
34
+ def run_sync(session, expected_result)
35
+ begin
36
+ number_records =
37
+ record_quantity(session, :left, :left) +
38
+ record_quantity(session, :conflict, :left) +
39
+ record_quantity(session, :same, :left) +
40
+ record_quantity(session, :right, :right)
41
+
42
+ number_differences =
43
+ record_quantity(session, :left, :left) +
44
+ record_quantity(session, :conflict, :left) +
45
+ record_quantity(session, :right, :right)
46
+
47
+ puts "\nSyncing (#{Initializer.configuration.options[:syncer]}, #{session.proxied? ? :proxied : :direct}) table big_scan (#{number_differences} differences in #{number_records} records)"
48
+
49
+ sync = TableSync.new(session, 'big_scan')
50
+ sync.progress_printer = RR::ScanProgressPrinters::ProgressBar
51
+ benchmark = Benchmark.measure { sync.run { |diff_type, row| } }
52
+ puts "\n time required: #{benchmark}"
53
+
54
+ {
55
+ :conflict_on_left => record_quantity(session, :conflict, :left),
56
+ :conflict_on_right => record_quantity(session, :conflict, :right),
57
+ :conflict_sum_on_left => record_sum(session, :conflict, :left),
58
+ :conflict_sum_on_right => record_sum(session, :conflict, :right),
59
+ :left_on_left => record_quantity(session, :left, :left),
60
+ :left_on_right => record_quantity(session, :left, :right),
61
+ :right_on_left => record_quantity(session, :right, :left),
62
+ :right_on_right => record_quantity(session, :right, :right)
63
+ }.should == expected_result
64
+ ensure
65
+ Committers::NeverCommitter.rollback_current_session
66
+ end
67
+ end
68
+
69
+ it "Proxied OneWaySync should sync correctly" do
70
+ ensure_proxy
71
+ Initializer.configuration = deep_copy(proxied_config)
72
+
73
+ Initializer.configuration.options = {
74
+ :committer => :never_commit,
75
+ :syncer => :one_way,
76
+ :direction => :right,
77
+ :delete => true,
78
+ :proxy_block_size => 100,
79
+ }
80
+
81
+ session = Session.new
82
+ expected_result = {
83
+ :conflict_on_left => record_quantity(session, :conflict, :left),
84
+ :conflict_on_right => record_quantity(session, :conflict, :right),
85
+ :conflict_sum_on_left => record_sum(session, :conflict, :left),
86
+ :conflict_sum_on_right => record_sum(session, :conflict, :left),
87
+ :left_on_left => record_quantity(session, :left, :left),
88
+ :left_on_right => record_quantity(session, :left, :left),
89
+ :right_on_left => 0,
90
+ :right_on_right => 0
91
+ }
92
+ run_sync session, expected_result
93
+ end
94
+
95
+ it "Direct OneWaySync should sync correctly" do
96
+ Initializer.configuration = deep_copy(standard_config)
97
+ Initializer.configuration.options = {
98
+ :committer => :never_commit,
99
+ :syncer => :one_way,
100
+ :direction => :right,
101
+ :delete => true
102
+ }
103
+
104
+ session = Session.new
105
+ expected_result = {
106
+ :conflict_on_left => record_quantity(session, :conflict, :left),
107
+ :conflict_on_right => record_quantity(session, :conflict, :right),
108
+ :conflict_sum_on_left => record_sum(session, :conflict, :left),
109
+ :conflict_sum_on_right => record_sum(session, :conflict, :left),
110
+ :left_on_left => record_quantity(session, :left, :left),
111
+ :left_on_right => record_quantity(session, :left, :left),
112
+ :right_on_left => 0,
113
+ :right_on_right => 0
114
+ }
115
+ run_sync session, expected_result
116
+ end
117
+
118
+ it "Proxied TwoWaySync should sync correctly" do
119
+ ensure_proxy
120
+ Initializer.configuration = deep_copy(proxied_config)
121
+ Initializer.configuration.options = {
122
+ :committer => :never_commit,
123
+ :syncer => :two_way,
124
+ :sync_conflict_handling => :right_wins,
125
+ :proxy_block_size => 100,
126
+ }
127
+
128
+ session = Session.new
129
+ expected_result = {
130
+ :conflict_on_left => record_quantity(session, :conflict, :left),
131
+ :conflict_on_right => record_quantity(session, :conflict, :right),
132
+ :conflict_sum_on_left => record_sum(session, :conflict, :right),
133
+ :conflict_sum_on_right => record_sum(session, :conflict, :right),
134
+ :left_on_left => record_quantity(session, :left, :left),
135
+ :left_on_right => record_quantity(session, :left, :left),
136
+ :right_on_left => record_quantity(session, :right, :right),
137
+ :right_on_right => record_quantity(session, :right, :right)
138
+ }
139
+ run_sync session, expected_result
140
+ end
141
+ end
@@ -0,0 +1,228 @@
1
+ require 'rake'
2
+ require 'benchmark'
3
+
4
+ require File.dirname(__FILE__) + '/../sim_helper'
5
+
6
+ # Prepares the database schema for the performance tests.
7
+ def prepare_schema
8
+ session = RR::Session.new
9
+
10
+ [:left, :right].each do |database|
11
+ [:big_scan, :big_rep, :big_rep_backup].each do |table|
12
+ session.send(database).drop_table table rescue nil
13
+ session.send(database).create_table table do |t|
14
+ t.column :diff_type, :string
15
+ t.string :text1, :text2, :text3, :text4
16
+ t.text :text5
17
+ t.binary :text6
18
+ t.integer :number1, :number2, :number3
19
+ t.float :number4
20
+ end rescue nil
21
+ end
22
+ end
23
+ end
24
+
25
+ BIG_SCAN_RECORD_NUMBER = 5000 # number of records to create for simulation
26
+ BIG_SCAN_SEED = 123 # random number seed to make simulation repeatable
27
+
28
+ # Percentage values for same, modified, left_only and right_only records in simulation
29
+ BIG_SCAN_SAME = 95
30
+ BIG_SCAN_MODIFIED = BIG_SCAN_SAME + 3
31
+ BIG_SCAN_LEFT_ONLY = BIG_SCAN_MODIFIED + 1 # difference to 100% will be right_only records
32
+
33
+ def big_scan_columns
34
+ @@big_scan_columns ||= nil
35
+ unless @@big_scan_columns
36
+ session = RR::Session.new
37
+ @@big_scan_columns = session.left.column_names('big_scan')
38
+ end
39
+ @@big_scan_columns
40
+ end
41
+
42
+ def text_columns
43
+ @@text_columns ||= big_scan_columns.select {|column_name| column_name =~ /^text/}
44
+ end
45
+
46
+ def number_columns
47
+ @@number_columns ||= big_scan_columns.select {|column_name| column_name =~ /^number/}
48
+ end
49
+
50
+ def random_attributes
51
+ attributes = {}
52
+ text_columns.each {|column_name| attributes[column_name] = "text#{rand(1000)}"}
53
+ number_columns.each {|column_name| attributes[column_name] = rand(1000)}
54
+ attributes
55
+ end
56
+
57
+ # Populates the big_scan tables with sample data.
58
+ def populate_scan_data()
59
+ session = RR::Session.new
60
+
61
+ [:left, :right].each {|database| session.send(database).execute "delete from big_scan"}
62
+
63
+ srand BIG_SCAN_SEED
64
+
65
+ puts "\nGenerating #{BIG_SCAN_RECORD_NUMBER} records in big_scan"
66
+ progress_bar = ProgressBar.new BIG_SCAN_RECORD_NUMBER
67
+
68
+ (1..BIG_SCAN_RECORD_NUMBER).each do |i|
69
+
70
+ # Updating progress bar
71
+ progress_bar.step
72
+
73
+ attributes = random_attributes
74
+ attributes['id'] = i
75
+
76
+ case rand(100)
77
+ when 0...BIG_SCAN_SAME
78
+ attributes['diff_type'] = 'same'
79
+ [:left, :right].each {|database| session.send(database).insert_record 'big_scan', attributes}
80
+ when BIG_SCAN_SAME...BIG_SCAN_MODIFIED
81
+ attributes['diff_type'] = 'conflict'
82
+ session.left.insert_record 'big_scan', attributes
83
+
84
+ attribute_name = text_columns[rand(text_columns.size)]
85
+ attributes[attribute_name] = attributes[attribute_name] + 'modified'
86
+ attribute_name = number_columns[rand(number_columns.size)]
87
+ attributes[attribute_name] = attributes[attribute_name] + 10000
88
+ session.right.insert_record 'big_scan', attributes
89
+ when BIG_SCAN_MODIFIED...BIG_SCAN_LEFT_ONLY
90
+ attributes['diff_type'] = 'left'
91
+ session.left.insert_record 'big_scan', attributes
92
+ else
93
+ attributes['diff_type'] = 'right'
94
+ session.right.insert_record 'big_scan', attributes
95
+ end
96
+ end
97
+ end
98
+
99
+ BIG_REP_CHANGE_NUMBER = 5000 # number of records to create for simulation
100
+ BIG_REP_SEED = 456 # random number seed to make simulation repeatable
101
+
102
+ # Percentage values for inserts, updates and deletes in simulation
103
+ BIG_REP_INSERT = 45
104
+ BIG_REP_UPDATE = BIG_REP_INSERT + 30 # different to 100% will be deletes
105
+
106
+ # Populates the big_rep tables with sample data and changes.
107
+ def populate_rep_data
108
+ session = RR::Session.new
109
+ initializer = RR::ReplicationInitializer.new session
110
+
111
+ # step 1: clear change log; ensure trigger, initialize big_rep table from data in big_scan
112
+ [:left, :right].each do |database|
113
+ initializer.drop_trigger(database, 'big_rep') rescue nil
114
+ session.send(database).execute "delete from big_rep"
115
+ session.send(database).execute "insert into big_rep select * from big_scan where diff_type = 'same'"
116
+ session.send(database).execute "delete from rr_pending_changes"
117
+ initializer.create_trigger(database, 'big_rep')
118
+ end
119
+
120
+ # step 2: generate changes
121
+
122
+ srand BIG_REP_SEED
123
+
124
+ # Keep tracks of the record ids in each database
125
+ all_ids = {}
126
+ all_ids[:left] = session.left.select_all("select id from big_rep").map {|row| row['id']}
127
+ all_ids[:right] = all_ids[:left].clone
128
+
129
+ # Next available id value
130
+ next_id = session.left.select_one("select max(id) + 1 as id from big_rep")['id'].to_i
131
+
132
+ puts "\nGenerating #{BIG_REP_CHANGE_NUMBER} changes in big_rep"
133
+ progress_bar = ProgressBar.new BIG_REP_CHANGE_NUMBER
134
+ (1..BIG_REP_CHANGE_NUMBER).each do
135
+
136
+ # Updating progress bar
137
+ progress_bar.step
138
+
139
+ database = [:left, :right].rand
140
+
141
+ case rand(100)
142
+ when 0...BIG_REP_INSERT
143
+ attributes = random_attributes
144
+ attributes['diff_type'] = 'insert'
145
+ attributes['id'] = next_id
146
+ all_ids[database] << next_id
147
+ next_id += 1
148
+ session.send(database).insert_record 'big_rep', attributes
149
+ when BIG_REP_INSERT...BIG_REP_UPDATE
150
+ id = all_ids[database].rand
151
+ attributes = session.send(database).select_one("select * from big_rep where id = '#{id}'")
152
+ column = number_columns[rand(number_columns.size)]
153
+ attributes[column] = rand(1000)
154
+ session.send(database).update_record 'big_rep', attributes
155
+ else
156
+ i = rand(all_ids[database].size)
157
+ id = all_ids[database].delete_at(i)
158
+ session.send(database).delete_record 'big_rep', 'id' => id
159
+ end
160
+ end
161
+
162
+ # step 3: move data into backup tables
163
+ [:left, :right].each do |database|
164
+ session.send(database).execute "delete from big_rep_backup"
165
+ session.send(database).execute "insert into big_rep_backup select * from big_rep"
166
+ session.send(database).drop_table "big_rep_pending_changes" rescue nil
167
+ session.send(database).execute "create table big_rep_pending_changes as select * from rr_pending_changes"
168
+ initializer.drop_trigger database, 'big_rep'
169
+ session.send(database).execute "delete from big_rep"
170
+ session.send(database).execute "delete from rr_pending_changes"
171
+ end
172
+ end
173
+
174
+ # Generates the sample data
175
+ def populate_data
176
+ populate_scan_data
177
+ populate_rep_data
178
+ end
179
+
180
+ # Prepares the database for the performance simulations
181
+ def prepare
182
+ prepare_schema
183
+ puts "time required: " + Benchmark.measure {populate_data}.to_s
184
+ end
185
+
186
+ namespace :sims do
187
+ namespace :performance do
188
+ desc "Prepare database"
189
+ task :prepare do
190
+ prepare
191
+ end
192
+
193
+ desc "Runs the big_scan simulation"
194
+ task :scan do
195
+ Spec::Runner::CommandLine.run(
196
+ Spec::Runner::OptionParser.parse(
197
+ ['--options', "spec/spec.opts", "./sims/performance/big_scan_spec.rb"],
198
+ $stdout, $stderr))
199
+ end
200
+
201
+ desc "Runs the big_sync simulation"
202
+ task :sync do
203
+ Spec::Runner::CommandLine.run(
204
+ Spec::Runner::OptionParser.parse(
205
+ ['--options', "spec/spec.opts", "./sims/performance/big_sync_spec.rb"],
206
+ $stdout, $stderr))
207
+ end
208
+
209
+ desc "Runs the big_rep simulation"
210
+ task :rep do
211
+ Spec::Runner::CommandLine.run(
212
+ Spec::Runner::OptionParser.parse(
213
+ ['--options', "spec/spec.opts", "./sims/performance/big_rep_spec.rb"],
214
+ $stdout, $stderr))
215
+ end
216
+
217
+ begin
218
+ require 'ruby-prof/task'
219
+ RubyProf::ProfileTask.new do |t|
220
+ t.test_files = FileList["./sims/performance/*_spec.rb"]
221
+ t.output_dir = 'profile'
222
+ t.printer = :flat
223
+ t.min_percent = 1
224
+ end
225
+ rescue LoadError
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,24 @@
1
+ # A helper class to print a text progress bar.
2
+ class ProgressBar
3
+ # Creates a new progress bar for a task consisting of +max+ steps.
4
+ # At 100% the progress bar will be +bar_length+ characters wide.
5
+ def initialize(max_steps, bar_length = 40)
6
+ @max_steps, @bar_length, @current_progress = max_steps, bar_length, 0
7
+ @steps_per_progress_bar_marker = @max_steps.to_f / @bar_length
8
+ @marker_counter = 0
9
+ puts "0%>#{'-' * (@bar_length - '0%>'.length - '100%>'.length)}>100%"
10
+ end
11
+
12
+ # Increases progress by +number_steps+ steps.
13
+ # If no argument provided, increase progress by 1 step.
14
+ def step(number_steps = 1)
15
+ @current_progress+= number_steps
16
+ if ((@current_progress - number_steps) / @steps_per_progress_bar_marker).to_i \
17
+ < (@current_progress / @steps_per_progress_bar_marker).to_i
18
+ @marker_counter += 1
19
+ putc '.'
20
+ puts if @marker_counter == @bar_length # after the last marker is printed, add a new_line
21
+ $stdout.flush
22
+ end
23
+ end
24
+ end