andyjeffries-rubyrep 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. data/History.txt +83 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +151 -0
  4. data/README.txt +37 -0
  5. data/bin/rubyrep +8 -0
  6. data/lib/rubyrep.rb +72 -0
  7. data/lib/rubyrep/base_runner.rb +195 -0
  8. data/lib/rubyrep/command_runner.rb +144 -0
  9. data/lib/rubyrep/committers/buffered_committer.rb +151 -0
  10. data/lib/rubyrep/committers/committers.rb +152 -0
  11. data/lib/rubyrep/configuration.rb +275 -0
  12. data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
  13. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
  14. data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
  15. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
  16. data/lib/rubyrep/database_proxy.rb +52 -0
  17. data/lib/rubyrep/direct_table_scan.rb +75 -0
  18. data/lib/rubyrep/generate_runner.rb +105 -0
  19. data/lib/rubyrep/initializer.rb +39 -0
  20. data/lib/rubyrep/log_helper.rb +30 -0
  21. data/lib/rubyrep/logged_change.rb +160 -0
  22. data/lib/rubyrep/logged_change_loader.rb +197 -0
  23. data/lib/rubyrep/noisy_connection.rb +80 -0
  24. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  25. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  26. data/lib/rubyrep/proxy_connection.rb +431 -0
  27. data/lib/rubyrep/proxy_cursor.rb +44 -0
  28. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  29. data/lib/rubyrep/proxy_runner.rb +89 -0
  30. data/lib/rubyrep/replication_difference.rb +100 -0
  31. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  32. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
  33. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  34. data/lib/rubyrep/replication_helper.rb +142 -0
  35. data/lib/rubyrep/replication_initializer.rb +327 -0
  36. data/lib/rubyrep/replication_run.rb +142 -0
  37. data/lib/rubyrep/replication_runner.rb +166 -0
  38. data/lib/rubyrep/replicators/replicators.rb +42 -0
  39. data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
  40. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  41. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  42. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  43. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  44. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  45. data/lib/rubyrep/scan_runner.rb +25 -0
  46. data/lib/rubyrep/session.rb +230 -0
  47. data/lib/rubyrep/sync_helper.rb +121 -0
  48. data/lib/rubyrep/sync_runner.rb +31 -0
  49. data/lib/rubyrep/syncers/syncers.rb +112 -0
  50. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  51. data/lib/rubyrep/table_scan.rb +54 -0
  52. data/lib/rubyrep/table_scan_helper.rb +46 -0
  53. data/lib/rubyrep/table_sorter.rb +70 -0
  54. data/lib/rubyrep/table_spec_resolver.rb +142 -0
  55. data/lib/rubyrep/table_sync.rb +90 -0
  56. data/lib/rubyrep/task_sweeper.rb +77 -0
  57. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  58. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  59. data/lib/rubyrep/uninstall_runner.rb +93 -0
  60. data/lib/rubyrep/version.rb +9 -0
  61. data/rubyrep +8 -0
  62. data/rubyrep.bat +4 -0
  63. data/setup.rb +1585 -0
  64. data/spec/base_runner_spec.rb +218 -0
  65. data/spec/buffered_committer_spec.rb +274 -0
  66. data/spec/command_runner_spec.rb +145 -0
  67. data/spec/committers_spec.rb +178 -0
  68. data/spec/configuration_spec.rb +203 -0
  69. data/spec/connection_extender_interface_spec.rb +141 -0
  70. data/spec/connection_extenders_registration_spec.rb +164 -0
  71. data/spec/database_proxy_spec.rb +48 -0
  72. data/spec/database_rake_spec.rb +40 -0
  73. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  74. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  75. data/spec/direct_table_scan_spec.rb +61 -0
  76. data/spec/dolphins.jpg +0 -0
  77. data/spec/generate_runner_spec.rb +84 -0
  78. data/spec/initializer_spec.rb +46 -0
  79. data/spec/log_helper_spec.rb +39 -0
  80. data/spec/logged_change_loader_spec.rb +68 -0
  81. data/spec/logged_change_spec.rb +470 -0
  82. data/spec/noisy_connection_spec.rb +78 -0
  83. data/spec/postgresql_replication_spec.rb +48 -0
  84. data/spec/postgresql_schema_support_spec.rb +212 -0
  85. data/spec/postgresql_support_spec.rb +63 -0
  86. data/spec/progress_bar_spec.rb +77 -0
  87. data/spec/proxied_table_scan_spec.rb +151 -0
  88. data/spec/proxy_block_cursor_spec.rb +197 -0
  89. data/spec/proxy_connection_spec.rb +423 -0
  90. data/spec/proxy_cursor_spec.rb +56 -0
  91. data/spec/proxy_row_cursor_spec.rb +66 -0
  92. data/spec/proxy_runner_spec.rb +70 -0
  93. data/spec/replication_difference_spec.rb +161 -0
  94. data/spec/replication_extender_interface_spec.rb +367 -0
  95. data/spec/replication_extenders_spec.rb +32 -0
  96. data/spec/replication_helper_spec.rb +178 -0
  97. data/spec/replication_initializer_spec.rb +509 -0
  98. data/spec/replication_run_spec.rb +443 -0
  99. data/spec/replication_runner_spec.rb +254 -0
  100. data/spec/replicators_spec.rb +36 -0
  101. data/spec/rubyrep_spec.rb +8 -0
  102. data/spec/scan_detail_reporter_spec.rb +119 -0
  103. data/spec/scan_progress_printers_spec.rb +68 -0
  104. data/spec/scan_report_printers_spec.rb +67 -0
  105. data/spec/scan_runner_spec.rb +50 -0
  106. data/spec/scan_summary_reporter_spec.rb +61 -0
  107. data/spec/session_spec.rb +253 -0
  108. data/spec/spec.opts +1 -0
  109. data/spec/spec_helper.rb +305 -0
  110. data/spec/strange_name_support_spec.rb +135 -0
  111. data/spec/sync_helper_spec.rb +169 -0
  112. data/spec/sync_runner_spec.rb +78 -0
  113. data/spec/syncers_spec.rb +171 -0
  114. data/spec/table_scan_helper_spec.rb +36 -0
  115. data/spec/table_scan_spec.rb +49 -0
  116. data/spec/table_sorter_spec.rb +30 -0
  117. data/spec/table_spec_resolver_spec.rb +111 -0
  118. data/spec/table_sync_spec.rb +140 -0
  119. data/spec/task_sweeper_spec.rb +47 -0
  120. data/spec/trigger_mode_switcher_spec.rb +83 -0
  121. data/spec/two_way_replicator_spec.rb +721 -0
  122. data/spec/two_way_syncer_spec.rb +256 -0
  123. data/spec/type_casting_cursor_spec.rb +50 -0
  124. data/spec/uninstall_runner_spec.rb +93 -0
  125. metadata +190 -0
@@ -0,0 +1,46 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'rubyrep'
4
+
5
+ module RR
6
+
7
+ # Some helper functions that are of use to all TableScan classes
8
+ module TableScanHelper
9
+ # Compares the primary keys of left_row and right_row to determine their rank.
10
+ # Assumes there is a function primary_key_names returning the array of primary keys
11
+ # that are relevant for this comparison
12
+ #
13
+ # Assumes that at least one of left_row and right_row is not nil
14
+ # A nil row counts as infinite.
15
+ # E. g. left_row is something and right_row is nil ==> left_row is smaller ==> return -1
16
+ def rank_rows(left_row, right_row)
17
+ raise "At least one of left_row and right_row must not be nil!" unless left_row or right_row
18
+ return -1 unless right_row
19
+ return 1 unless left_row
20
+ rank = 0
21
+ primary_key_names.any? do |key|
22
+ if left_row[key].kind_of?(String)
23
+ # When databases order strings, then 'a' < 'A' while for Ruby 'A' < 'a'
24
+ # ==> Use a combination of case sensitive and case insensitive comparing to
25
+ # reproduce the database behaviour.
26
+ rank = left_row[key].casecmp(right_row[key]) # deal with 'a' to 'B' comparisons
27
+ rank = -(left_row[key] <=> right_row[key]) if rank == 0 # deal with 'a' to 'A' comparisons
28
+ else
29
+ rank = left_row[key] <=> right_row[key]
30
+ end
31
+ rank != 0
32
+ end
33
+ rank
34
+ end
35
+
36
+ # Returns the correct class for the table scan based on the type of the
37
+ # session (proxied or direct).
38
+ def self.scan_class(session)
39
+ if session.proxied?
40
+ ProxiedTableScan
41
+ else
42
+ DirectTableScan
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,70 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'tsort'
4
+ require 'rubyrep'
5
+
6
+ module RR
7
+ # This class sorts a given list of tables so that tables referencing other
8
+ # tables via foreign keys are placed behind those referenced tables.
9
+ #
10
+ # Rationale:
11
+ # If tables are sorted in that sequence, the risk of foreign key violations is
12
+ # smaller.
13
+ class TableSorter
14
+ include TSort
15
+
16
+ # The active +Session+
17
+ attr_accessor :session
18
+
19
+ # The list of table names to be ordered
20
+ attr_accessor :tables
21
+
22
+ # The table dependencies.
23
+ # Format as described e. g. here: PostgreSQLExtender#referenced_tables
24
+ def referenced_tables
25
+ unless @referenced_tables
26
+ @referenced_tables = session.left.referenced_tables(tables)
27
+
28
+ # Strip away all unrelated tables
29
+ @referenced_tables.each_pair do |table, references|
30
+ references.delete_if do |reference|
31
+ not tables.include? reference
32
+ end
33
+ end
34
+ end
35
+ @referenced_tables
36
+ end
37
+
38
+ # Yields each table.
39
+ # For details see standard library: TSort#sort_each_node.
40
+ def tsort_each_node
41
+ referenced_tables.each_key do |table|
42
+ yield table
43
+ end
44
+ end
45
+
46
+ # Yields all tables that are references by +table+.
47
+ def tsort_each_child(table)
48
+ referenced_tables[table].each do |reference|
49
+ yield reference
50
+ end
51
+ end
52
+
53
+ def sort
54
+ # Note:
55
+ # We should not use TSort#tsort as this one throws an exception if
56
+ # there are cyclic redundancies.
57
+ # (Our goal is to just get the best ordering that is possible and then
58
+ # take our chances.)
59
+ strongly_connected_components.flatten
60
+ end
61
+
62
+ # Initializes the TableSorter
63
+ # * session: The active +Session+ instance
64
+ # * tables: an array of table names
65
+ def initialize(session, tables)
66
+ self.session = session
67
+ self.tables = tables
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,142 @@
1
+ module RR
2
+
3
+ # Resolves table specifications as provided e. g. in the command line of rrscan
4
+ class TableSpecResolver
5
+
6
+ # The +Session+ instance from which the table specifications are resolved.
7
+ attr_accessor :session
8
+
9
+ # Returns the array of tables of the specified database. Caches the table array.
10
+ # * database: either :+left+ or :+right+
11
+ def tables(database)
12
+ @table_cache ||= {}
13
+ unless @table_cache[database]
14
+ @table_cache[database] = session.send(database).tables
15
+ end
16
+ @table_cache[database]
17
+ end
18
+
19
+ # Creates a resolver that works based on the given +Session+ instance.
20
+ def initialize(session)
21
+ self.session = session
22
+ end
23
+
24
+ # Returns all those tables from the given table_pairs that do not exist.
25
+ # * +table_pairs+: same as described at #table_pairs_without_excluded
26
+ #
27
+ # Returns:
28
+ # A hash with keys :+left+ and +:right+, with the value for each key being
29
+ # an array of non-existing tables for the according database.
30
+ # The keys only exist if there are according missing tables.
31
+ def non_existing_tables(table_pairs)
32
+ [:left, :right].inject({}) do |memo, database|
33
+ found_tables = table_pairs.inject([]) do |phantom_tables, table_pair|
34
+ phantom_tables << table_pair[database] unless tables(database).include?(table_pair[database])
35
+ phantom_tables
36
+ end
37
+ memo[database] = found_tables unless found_tables.empty?
38
+ memo
39
+ end
40
+ end
41
+
42
+ # Resolves the given array of table specificifications.
43
+ # Table specifications are either
44
+ # * strings as produced by BaseRunner#get_options or
45
+ # * actual regular expressions
46
+ # If +excluded_table_specs+ is provided, removes all tables that match it
47
+ # (even if otherwise matching +included_table_specs+).
48
+ #
49
+ # If +verify+ is +true+, raises an exception if any non-existing tables are
50
+ # specified.
51
+ #
52
+ # Returns an array of table name pairs in Hash form.
53
+ # For example something like
54
+ # [{:left => 'my_table', :right => 'my_table_backup'}]
55
+ #
56
+ # Takes care that a table is only returned once.
57
+ def resolve(included_table_specs, excluded_table_specs = [], verify = true)
58
+ table_pairs = expand_table_specs(included_table_specs, verify)
59
+ table_pairs = table_pairs_without_duplicates(table_pairs)
60
+ table_pairs = table_pairs_without_excluded(table_pairs, excluded_table_specs)
61
+
62
+ if verify
63
+ non_existing_tables = non_existing_tables(table_pairs)
64
+ unless non_existing_tables.empty?
65
+ raise "non-existing tables specified: #{non_existing_tables.inspect}"
66
+ end
67
+ end
68
+
69
+ table_pairs
70
+ end
71
+
72
+ # Helper for #resolve
73
+ # Expands table specifications into table pairs.
74
+ # Parameters:
75
+ # * +table_specs+:
76
+ # An array of table specifications as described under #resolve.
77
+ # * +verify+:
78
+ # If +true+, table specs in regexp format only resolve if the table exists
79
+ # in left and right database.
80
+ # Return value: refer to #resolve for a detailed description
81
+ def expand_table_specs(table_specs, verify)
82
+ table_pairs = []
83
+ table_specs.each do |table_spec|
84
+
85
+ # If it is a regexp, convert it in an according string
86
+ table_spec = table_spec.inspect if table_spec.kind_of? Regexp
87
+
88
+ case table_spec
89
+ when /^\/.*\/$/ # matches e. g. '/^user/'
90
+ table_spec = table_spec.sub(/^\/(.*)\/$/,'\1') # remove leading and trailing slash
91
+ matching_tables = tables(:left).grep(Regexp.new(table_spec, Regexp::IGNORECASE))
92
+ matching_tables.each do |table|
93
+ if !verify or tables(:right).include? table
94
+ table_pairs << {:left => table, :right => table}
95
+ end
96
+ end
97
+ when /.+,.+/ # matches e. g. 'users,users_backup'
98
+ pair = table_spec.match(/(.*),(.*)/)[1..2].map { |str| str.strip }
99
+ table_pairs << {:left => pair[0], :right => pair[1]}
100
+ else # everything else: just a normal table
101
+ table_pairs << {:left => table_spec.strip, :right => table_spec.strip}
102
+ end
103
+ end
104
+ table_pairs
105
+ end
106
+ private :expand_table_specs
107
+
108
+ # Helper for #resolve
109
+ # Takes given table_pairs and removes all tables that are excluded.
110
+ # Returns the result.
111
+ # Both the given and the returned table_pairs is an array of hashes with
112
+ # * :+left+: name of the left table
113
+ # * :+right+: name of the corresponding right table
114
+ # +excluded_table_specs+ is the array of table specifications to be excluded.
115
+ def table_pairs_without_excluded(table_pairs, excluded_table_specs)
116
+ excluded_tables = expand_table_specs(excluded_table_specs, false).map do |table_pair|
117
+ table_pair[:left]
118
+ end
119
+ table_pairs.select {|table_pair| not excluded_tables.include? table_pair[:left]}
120
+ end
121
+ private :table_pairs_without_excluded
122
+
123
+ # Helper for #resolve
124
+ # Takes given table_pairs and removes all duplicates.
125
+ # Returns the result.
126
+ # Both the given and the returned table_pairs is an array of hashes with
127
+ # * :+left+: name of the left table
128
+ # * :+right+: name of the corresponding right table
129
+ def table_pairs_without_duplicates(table_pairs)
130
+ processed_left_tables = {}
131
+ resulting_table_pairs = []
132
+ table_pairs.each do |table_pair|
133
+ unless processed_left_tables.include? table_pair[:left]
134
+ resulting_table_pairs << table_pair
135
+ processed_left_tables[table_pair[:left]] = true
136
+ end
137
+ end
138
+ resulting_table_pairs
139
+ end
140
+ private :table_pairs_without_duplicates
141
+ end
142
+ end
@@ -0,0 +1,90 @@
1
+ module RR
2
+
3
+ # Synchronizes the data of two tables.
4
+ class TableSync < TableScan
5
+
6
+ # Instance of SyncHelper
7
+ attr_accessor :helper
8
+
9
+ # Returns a hash of sync options for this table sync.
10
+ def sync_options
11
+ @sync_options ||= session.configuration.options_for_table(left_table)
12
+ end
13
+
14
+ # Creates a new TableSync instance
15
+ # * session: a Session object representing the current database session
16
+ # * left_table: name of the table in the left database
17
+ # * right_table: name of the table in the right database. If not given, same like left_table
18
+ def initialize(session, left_table, right_table = nil)
19
+ super
20
+ end
21
+
22
+ # Executes the specified sync hook
23
+ # * +hook_id+: either :+before_table_sync+ or :+after_table_sync+
24
+ def execute_sync_hook(hook_id)
25
+ hook = sync_options[hook_id]
26
+ if hook
27
+ if hook.respond_to?(:call)
28
+ hook.call(helper)
29
+ else
30
+ [:left, :right].each do |database|
31
+ session.send(database).execute hook
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # Calls the event filter for the give table difference.
38
+ # * +type+: type of difference
39
+ # * +row+: the differing row
40
+ # Refer to DirectTableScan#run for full description of +type+ and +row+.
41
+ # Returns +true+ if syncing of the difference should *not* proceed.
42
+ def event_filtered?(type, row)
43
+ event_filter = sync_options[:event_filter]
44
+ if event_filter && event_filter.respond_to?(:before_sync)
45
+ not event_filter.before_sync(
46
+ helper.left_table,
47
+ helper.extract_key([row].flatten.first),
48
+ helper,
49
+ type,
50
+ row
51
+ )
52
+ else
53
+ false
54
+ end
55
+ end
56
+
57
+ # Executes the table sync. If a block is given, yields each difference with
58
+ # the following 2 parameters
59
+ # * +type+
60
+ # * +row+
61
+ # Purpose: enable display of progress information.
62
+ # See DirectTableScan#run for full description of yielded parameters.
63
+ def run
64
+ success = false
65
+
66
+ scan_class = TableScanHelper.scan_class(session)
67
+ scan = scan_class.new(session, left_table, right_table)
68
+ scan.progress_printer = progress_printer
69
+
70
+ self.helper = SyncHelper.new(self)
71
+ syncer = Syncers.configured_syncer(sync_options).new(helper)
72
+
73
+ execute_sync_hook :before_table_sync
74
+
75
+ scan.run do |type, row|
76
+ yield type, row if block_given? # To enable progress reporting
77
+ unless event_filtered?(type, row)
78
+ syncer.sync_difference type, row
79
+ end
80
+ end
81
+
82
+ execute_sync_hook :after_table_sync
83
+
84
+ success = true # considered to be successful if we get till here
85
+ ensure
86
+ helper.finalize success if helper
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,77 @@
1
+ module RR
2
+
3
+ # Monitors and cancels stalled tasks
4
+ class TaskSweeper
5
+
6
+ # Executes the give block in a separate Thread.
7
+ # Returns if block is finished or stalled.
8
+ # The block must call regular #ping to announce it is not stalled.
9
+ # * +timeout_period+:
10
+ # Maximum time (in seonds) without ping, after which a task is considered stalled.
11
+ # Returns the created sweeper (allows checking if task was terminated).
12
+ def self.timeout(timeout_period)
13
+ sweeper = TaskSweeper.new(timeout_period)
14
+ sweeper.send(:timeout) {yield sweeper}
15
+ sweeper
16
+ end
17
+
18
+ # Time in seconds after which a task is considered stalled. Timer is reset
19
+ # by calling #ping.
20
+ attr_accessor :timeout_period
21
+
22
+ # Must be called by the executed task to announce it is still alive
23
+ def ping
24
+ self.last_ping = Time.now
25
+ end
26
+
27
+ # Returns +true+ if the task was timed out.
28
+ # The terminated task is expected to free all resources and exit.
29
+ def terminated?
30
+ terminated
31
+ end
32
+
33
+ # Waits without timeout till the task executing thread is finished
34
+ def join
35
+ thread && thread.join
36
+ end
37
+
38
+ # Creates a new TaskSweeper
39
+ # * +timeout_period+: timeout value in seconds
40
+ def initialize(timeout_period)
41
+ self.timeout_period = timeout_period
42
+ self.terminated = false
43
+ self.last_ping = Time.now
44
+ end
45
+
46
+ protected
47
+
48
+ # Time of last ping
49
+ attr_accessor :last_ping
50
+
51
+ # Set to +true+ if the executed task has timed out
52
+ attr_accessor :terminated
53
+
54
+ # The task executing thread
55
+ attr_accessor :thread
56
+
57
+ # Executes the given block and times it out if stalled.
58
+ def timeout
59
+ exception = nil
60
+ self.thread = Thread.new do
61
+ begin
62
+ yield
63
+ rescue Exception => e
64
+ # save exception so it can be rethrown outside of the thread
65
+ exception = e
66
+ end
67
+ end
68
+ while self.thread.join(self.timeout_period) == nil do
69
+ if self.last_ping < Time.now - self.timeout_period
70
+ self.terminated = true
71
+ break
72
+ end
73
+ end
74
+ raise exception if exception
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,63 @@
1
+ require 'set'
2
+
3
+ module RR
4
+
5
+ # Switches rubyrep triggers between "exclude rubyrep activity" modes.
6
+ class TriggerModeSwitcher
7
+
8
+ # Keeps track of all the triggers.
9
+ # This is a hash with 2 keys: :+left+ and :+right+.
10
+ # Each of these entries is a Set containing table names.
11
+ def triggers
12
+ @triggers ||= {
13
+ :left => Set.new,
14
+ :right => Set.new
15
+ }
16
+ end
17
+
18
+ # The active Session
19
+ attr_accessor :session
20
+
21
+ def initialize(session)
22
+ self.session = session
23
+ end
24
+
25
+ # Does the actual switching of the trigger mode.
26
+ # * +database+: either :+left+ or :+right+
27
+ # * +table+: name of the table
28
+ # * +exclude_rr_activity+: the new trigger mode (either +true+ or +false+)
29
+ def switch_trigger_mode(database, table, exclude_rr_activity)
30
+ options = session.configuration.options
31
+ if session.send(database).replication_trigger_exists? "#{options[:rep_prefix]}_#{table}", table
32
+ params = {
33
+ :trigger_name => "#{options[:rep_prefix]}_#{table}",
34
+ :table => table,
35
+ :keys => session.send(database).primary_key_names(table),
36
+ :log_table => "#{options[:rep_prefix]}_pending_changes",
37
+ :activity_table => "#{options[:rep_prefix]}_running_flags",
38
+ :key_sep => options[:key_sep],
39
+ :exclude_rr_activity => exclude_rr_activity,
40
+ }
41
+ session.send(database).create_or_replace_replication_trigger_function(params)
42
+ end
43
+ end
44
+
45
+ # Switches the trigger of the named table to "exclude rubyrep activity" mode.
46
+ # Only switches if it didn't do so already for the table.
47
+ # * +database+: either :+left+ or :+right+
48
+ # * +table+: name of the table
49
+ def exclude_rr_activity(database, table)
50
+ switch_trigger_mode(database, table, true) if triggers[database].add? table
51
+ end
52
+
53
+ # Restores all switched triggers to not exclude rubyrep activity
54
+ def restore_triggers
55
+ [:left, :right].each do |database|
56
+ triggers[database].each do |table|
57
+ switch_trigger_mode database, table, false
58
+ end
59
+ triggers[database].clear
60
+ end
61
+ end
62
+ end
63
+ end