rubyrep 1.0.9 → 1.1.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 +5 -0
- data/config/hoe.rb +2 -2
- data/config/requirements.rb +1 -1
- data/lib/rubyrep/committers/buffered_committer.rb +20 -15
- data/lib/rubyrep/configuration.rb +11 -7
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +1 -1
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +16 -34
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +66 -0
- data/lib/rubyrep/proxy_connection.rb +30 -3
- data/lib/rubyrep/replication_helper.rb +33 -3
- data/lib/rubyrep/replication_run.rb +20 -1
- data/lib/rubyrep/replicators/replicators.rb +6 -1
- data/lib/rubyrep/replicators/two_way_replicator.rb +12 -20
- data/lib/rubyrep/sync_helper.rb +14 -6
- data/lib/rubyrep/syncers/syncers.rb +3 -3
- data/lib/rubyrep/syncers/two_way_syncer.rb +4 -4
- data/lib/rubyrep/table_sync.rb +23 -1
- data/lib/rubyrep/version.rb +2 -2
- data/spec/configuration_spec.rb +5 -0
- data/spec/connection_extender_interface_spec.rb +16 -21
- data/spec/db_specific_connection_extenders_spec.rb +1 -1
- data/spec/logged_change_spec.rb +2 -2
- data/spec/noisy_connection_spec.rb +7 -9
- data/spec/postgresql_schema_support_spec.rb +5 -2
- data/spec/postgresql_support_spec.rb +8 -2
- data/spec/proxy_connection_spec.rb +34 -30
- data/spec/replication_helper_spec.rb +40 -0
- data/spec/replication_run_spec.rb +114 -3
- data/spec/replicators_spec.rb +5 -0
- data/spec/strange_name_support_spec.rb +14 -3
- data/spec/sync_helper_spec.rb +11 -4
- data/spec/syncers_spec.rb +6 -6
- data/spec/table_sync_spec.rb +60 -4
- data/spec/two_way_replicator_spec.rb +18 -38
- data/spec/two_way_syncer_spec.rb +6 -6
- data/spec/type_casting_cursor_spec.rb +8 -8
- data/tasks/database.rake +6 -0
- metadata +4 -4
data/History.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
== 1.1.0 2009-12-15
|
2
|
+
|
3
|
+
* Feature: filtering of sync / replication events with custom specified functionality
|
4
|
+
* Feature: compatibility with JRuby 1.4.0 and ActiveRecord 2.3.5
|
5
|
+
|
1
6
|
== 1.0.9 2009-10-14
|
2
7
|
|
3
8
|
* Bug fix: ReplicationRun#run should load only one change log record to figure out if there are pending changes
|
data/config/hoe.rb
CHANGED
@@ -59,8 +59,8 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
59
59
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
|
60
60
|
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
61
61
|
p.extra_deps = [
|
62
|
-
['activesupport', '>= 2.
|
63
|
-
['activerecord' , '>= 2.
|
62
|
+
['activesupport', '>= 2.3.5'],
|
63
|
+
['activerecord' , '>= 2.3.5']
|
64
64
|
]
|
65
65
|
|
66
66
|
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
data/config/requirements.rb
CHANGED
@@ -16,11 +16,13 @@ module RR
|
|
16
16
|
|
17
17
|
# Switches the trigger mode of the specified +table+ in the specified
|
18
18
|
# +database+ to ignore rubyrep activity.
|
19
|
+
# * +database+: identifying the database (either :+left+ or :+right+)
|
20
|
+
# * +table+: name of the table
|
19
21
|
def exclude_rr_activity(database, table)
|
20
22
|
trigger_mode_switcher.exclude_rr_activity database, table
|
21
23
|
end
|
22
24
|
|
23
|
-
# Returns the
|
25
|
+
# Returns the TriggerModeSwitcher (creates it if necessary)
|
24
26
|
def trigger_mode_switcher
|
25
27
|
@trigger_mode_switcher ||= TriggerModeSwitcher.new session
|
26
28
|
end
|
@@ -96,36 +98,39 @@ module RR
|
|
96
98
|
end
|
97
99
|
|
98
100
|
# A new committer is created for each table sync.
|
99
|
-
#
|
101
|
+
# * session: a Session object representing the current database session
|
100
102
|
def initialize(session)
|
101
103
|
super
|
102
104
|
begin_db_transactions
|
103
105
|
end
|
104
106
|
|
105
|
-
# Inserts the specified record in the specified
|
106
|
-
# +
|
107
|
-
# +
|
107
|
+
# Inserts the specified record in the specified database.
|
108
|
+
# * +database+: identifying the database (either :+left+ or :+right+)
|
109
|
+
# * +table+: name of the table
|
110
|
+
# * +values+: a hash of column_name => value pairs.
|
108
111
|
def insert_record(database, table, values)
|
109
112
|
exclude_rr_activity database, table
|
110
113
|
super
|
111
114
|
commit
|
112
115
|
end
|
113
116
|
|
114
|
-
# Updates the specified record in the specified
|
115
|
-
# +
|
116
|
-
# +
|
117
|
-
# +
|
118
|
-
#
|
117
|
+
# Updates the specified record in the specified database.
|
118
|
+
# * +database+: identifying the database (either :+left+ or :+right+)
|
119
|
+
# * +table+: name of the table
|
120
|
+
# * +values+: a hash of column_name => value pairs.
|
121
|
+
# * +old_key+:
|
122
|
+
# A column_name => value hash identifying original primary key.
|
123
|
+
# If +nil+, then the primary key must be contained in +values+.
|
119
124
|
def update_record(database, table, values, old_key = nil)
|
120
125
|
exclude_rr_activity database, table
|
121
126
|
super
|
122
127
|
commit
|
123
128
|
end
|
124
129
|
|
125
|
-
# Deletes the specified record in the specified
|
126
|
-
# +
|
127
|
-
# +
|
128
|
-
# values
|
130
|
+
# Deletes the specified record in the specified database.
|
131
|
+
# * +database+: identifying the database (either :+left+ or :+right+)
|
132
|
+
# * +table+: name of the table
|
133
|
+
# * +values+: a hash of column_name => value pairs (must only contain primary key columns).
|
129
134
|
def delete_record(database, table, values)
|
130
135
|
exclude_rr_activity database, table
|
131
136
|
super
|
@@ -133,7 +138,7 @@ module RR
|
|
133
138
|
end
|
134
139
|
|
135
140
|
# Is called after the last insert / update / delete query.
|
136
|
-
# +success
|
141
|
+
# * +success+: should be true if there were no problems, false otherwise.
|
137
142
|
def finalize(success = true)
|
138
143
|
if success
|
139
144
|
commit_db_transactions
|
@@ -239,14 +239,18 @@ module RR
|
|
239
239
|
resulting_options.merge! table_options[1] if match
|
240
240
|
end
|
241
241
|
|
242
|
-
# Merge the default syncer options in
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
242
|
+
# Merge the default syncer& replicator options in
|
243
|
+
[
|
244
|
+
Syncers.configured_syncer(resulting_options),
|
245
|
+
Replicators.configured_replicator(resulting_options)
|
246
|
+
].each do |processor_class|
|
247
|
+
if processor_class.respond_to? :default_options
|
248
|
+
default_processor_options = processor_class.default_options.clone
|
249
|
+
else
|
250
|
+
default_processor_options = {}
|
251
|
+
end
|
252
|
+
resulting_options = default_processor_options.merge!(resulting_options)
|
248
253
|
end
|
249
|
-
resulting_options = default_syncer_options.merge! resulting_options
|
250
254
|
|
251
255
|
resulting_options
|
252
256
|
end
|
@@ -76,7 +76,7 @@ module RR
|
|
76
76
|
# Hack to get Postgres schema support under JRuby to par with the standard
|
77
77
|
# ruby version
|
78
78
|
if RUBY_PLATFORM =~ /java/ and config[:adapter].to_sym == :postgresql
|
79
|
-
connection.extend RR::ConnectionExtenders::
|
79
|
+
connection.extend RR::ConnectionExtenders::PostgreSQLExtender
|
80
80
|
connection.initialize_search_path
|
81
81
|
end
|
82
82
|
|
@@ -55,44 +55,26 @@ module RR
|
|
55
55
|
result
|
56
56
|
end
|
57
57
|
end
|
58
|
+
end
|
59
|
+
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the list of all column definitions for a table.
|
74
|
-
def columns(table_name, name = nil)
|
75
|
-
# Limit, precision, and scale are all handled by the superclass.
|
76
|
-
column_definitions(table_name).collect do |name, type, default, notnull|
|
77
|
-
JdbcPostgreSQLColumn.new(name, default, type, notnull == 'f')
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Sets the schema search path as per configuration parameters
|
82
|
-
def initialize_search_path
|
83
|
-
execute "SET search_path TO #{config[:schema_search_path]}" if config[:schema_search_path]
|
61
|
+
# activerecord-jdbc-adapter 0.9.1 (7b3f3eca08149567070837fad63696052dc36cd6)
|
62
|
+
# improves SQLite binary support by overwriting the global string_to_binary
|
63
|
+
# methods.
|
64
|
+
# This appears to break binary support for MySQL.
|
65
|
+
# And here comes the monkey patch to revert it again...
|
66
|
+
require 'active_record/connection_adapters/jdbc_adapter_spec'
|
67
|
+
require 'jdbc_adapter/jdbc_sqlite3'
|
68
|
+
module ::ActiveRecord
|
69
|
+
module ConnectionAdapters
|
70
|
+
class JdbcColumn < Column
|
71
|
+
def self.string_to_binary(value)
|
72
|
+
value
|
84
73
|
end
|
85
74
|
|
86
|
-
|
87
|
-
|
88
|
-
#
|
89
|
-
# Monkeypatched as activerecord-jdbcpostgresql-adapter (at least in version
|
90
|
-
# 0.8.2) does otherwise "loose" the microseconds when writing Time values
|
91
|
-
# to the database.
|
92
|
-
def quoted_date(value)
|
93
|
-
"#{value.strftime("%Y-%m-%d %H:%M:%S")}#{value.respond_to?(:usec) ? ".#{value.usec.to_s.rjust(6, '0')}" : ""}"
|
75
|
+
def self.binary_to_string(value)
|
76
|
+
value
|
94
77
|
end
|
95
78
|
end
|
96
79
|
end
|
97
80
|
end
|
98
|
-
|
@@ -107,6 +107,15 @@ module RR
|
|
107
107
|
SQL
|
108
108
|
end
|
109
109
|
|
110
|
+
# Disables schema extraction from table names by overwriting the according
|
111
|
+
# ActiveRecord method.
|
112
|
+
# Necessary to support table names containing dots (".").
|
113
|
+
# (This is possible as rubyrep exclusively uses the search_path setting to
|
114
|
+
# support PostgreSQL schemas.)
|
115
|
+
def extract_pg_identifier_from_name(name)
|
116
|
+
return name, nil
|
117
|
+
end
|
118
|
+
|
110
119
|
# Returns an ordered list of primary key column names of the given table
|
111
120
|
def primary_key_names(table)
|
112
121
|
row = self.select_one(<<-end_sql)
|
@@ -177,6 +186,63 @@ module RR
|
|
177
186
|
result
|
178
187
|
end
|
179
188
|
|
189
|
+
# Sets the schema search path as per configuration parameters
|
190
|
+
def initialize_search_path
|
191
|
+
execute "SET search_path TO #{config[:schema_search_path] || 'public'}"
|
192
|
+
end
|
193
|
+
|
194
|
+
# *** Moneky patch***
|
195
|
+
# Returns the column objects for the named table.
|
196
|
+
# Fixes JRuby schema support
|
197
|
+
def columns(table_name, name = nil)
|
198
|
+
jdbc_connection = @connection.connection # the actual JDBC DatabaseConnection
|
199
|
+
@unquoted_schema ||= select_one("show search_path")['search_path']
|
200
|
+
|
201
|
+
# check if table exists
|
202
|
+
table_results = jdbc_connection.meta_data.get_tables(
|
203
|
+
jdbc_connection.catalog,
|
204
|
+
@unquoted_schema,
|
205
|
+
table_name,
|
206
|
+
["TABLE","VIEW","SYNONYM"].to_java(:string)
|
207
|
+
)
|
208
|
+
table_exists = table_results.next
|
209
|
+
table_results.close
|
210
|
+
raise "table '#{table_name}' not found" unless table_exists
|
211
|
+
|
212
|
+
# get ResultSet for columns of table
|
213
|
+
column_results = jdbc_connection.meta_data.get_columns(
|
214
|
+
jdbc_connection.catalog,
|
215
|
+
@unquoted_schema,
|
216
|
+
table_name,
|
217
|
+
nil
|
218
|
+
)
|
219
|
+
|
220
|
+
# create the Column objects
|
221
|
+
columns = []
|
222
|
+
while column_results.next
|
223
|
+
|
224
|
+
# generate type clause
|
225
|
+
type_clause = column_results.get_string('TYPE_NAME')
|
226
|
+
precision = column_results.get_int('COLUMN_SIZE')
|
227
|
+
scale = column_results.get_int('DECIMAL_DIGITS')
|
228
|
+
if precision > 0
|
229
|
+
type_clause += "(#{precision}#{scale > 0 ? ",#{scale}" : ""})"
|
230
|
+
end
|
231
|
+
|
232
|
+
# create column
|
233
|
+
columns << ::ActiveRecord::ConnectionAdapters::JdbcColumn.new(
|
234
|
+
@config,
|
235
|
+
column_results.get_string('COLUMN_NAME'),
|
236
|
+
column_results.get_string('COLUMN_DEF'),
|
237
|
+
type_clause,
|
238
|
+
column_results.get_string('IS_NULLABLE').strip == "NO"
|
239
|
+
)
|
240
|
+
end
|
241
|
+
column_results.close
|
242
|
+
|
243
|
+
columns
|
244
|
+
end if RUBY_PLATFORM =~ /java/
|
245
|
+
|
180
246
|
# *** Monkey patch***
|
181
247
|
# Returns the list of a table's column names, data types, and default values.
|
182
248
|
# This overwrites the according ActiveRecord::PostgreSQLAdapter method
|
@@ -184,19 +184,46 @@ module RR
|
|
184
184
|
# * first build the query based on options forwarded to #table_select_query
|
185
185
|
# +options+ is a hash with
|
186
186
|
# * :+query+: executes the given query
|
187
|
-
# * :+type_cast+:
|
188
|
-
#
|
187
|
+
# * :+type_cast+:
|
188
|
+
# Unless explicitely disabled with +false+, build type casting cursor
|
189
|
+
# around result.
|
190
|
+
# * :+table+:
|
191
|
+
# Name of the table from which to read data.
|
192
|
+
# Required unless type casting is disabled.
|
189
193
|
# * further options as taken by #table_select_query to build the query
|
190
194
|
# * :+row_buffer_size+:
|
191
195
|
# Integer controlling how many rows a read into memory at one time.
|
192
196
|
def select_cursor(options)
|
193
197
|
cursor = ResultFetcher.new(self, options)
|
194
|
-
|
198
|
+
unless options[:type_cast] == false
|
195
199
|
cursor = TypeCastingCursor.new(self, options[:table], cursor)
|
196
200
|
end
|
197
201
|
cursor
|
198
202
|
end
|
203
|
+
|
204
|
+
# Reads the designated record from the database.
|
205
|
+
# Refer to #select_cursor for details parameter description.
|
206
|
+
# Returns the first matching row (column_name => value hash or +nil+).
|
207
|
+
def select_record(options)
|
208
|
+
cursor = select_cursor({:row_buffer_size => 1}.merge(options))
|
209
|
+
row = cursor.next? ? cursor.next_row : nil
|
210
|
+
cursor.clear
|
211
|
+
row
|
212
|
+
end
|
199
213
|
|
214
|
+
# Reads the designated records from the database.
|
215
|
+
# Refer to #select_cursor for details parameter description.
|
216
|
+
# Returns an array of matching rows (column_name => value hashes).
|
217
|
+
def select_records(options)
|
218
|
+
cursor = select_cursor(options)
|
219
|
+
rows = []
|
220
|
+
while cursor.next?
|
221
|
+
rows << cursor.next_row
|
222
|
+
end
|
223
|
+
cursor.clear
|
224
|
+
rows
|
225
|
+
end
|
226
|
+
|
200
227
|
# Create a session on the proxy side according to provided configuration hash.
|
201
228
|
# +config+ is a hash as described by ActiveRecord::Base#establish_connection
|
202
229
|
def initialize(config)
|
@@ -16,6 +16,16 @@ module RR
|
|
16
16
|
# Current options
|
17
17
|
def options; @options ||= session.configuration.options; end
|
18
18
|
|
19
|
+
# Returns the options for the specified table name.
|
20
|
+
# * +table+: name of the table (left database version)
|
21
|
+
def options_for_table(table)
|
22
|
+
@options_for_table ||= {}
|
23
|
+
unless @options_for_table.include? table
|
24
|
+
@options_for_table[table] = session.configuration.options_for_table(table)
|
25
|
+
end
|
26
|
+
@options_for_table[table]
|
27
|
+
end
|
28
|
+
|
19
29
|
# Delegates to Session#corresponding_table
|
20
30
|
def corresponding_table(db_arm, table); session.corresponding_table(db_arm, table); end
|
21
31
|
|
@@ -25,17 +35,17 @@ module RR
|
|
25
35
|
committer.new_transaction?
|
26
36
|
end
|
27
37
|
|
28
|
-
# Delegates to
|
38
|
+
# Delegates to Committers::BufferedCommitter#insert_record
|
29
39
|
def insert_record(database, table, values)
|
30
40
|
committer.insert_record(database, table, values)
|
31
41
|
end
|
32
42
|
|
33
|
-
# Delegates to
|
43
|
+
# Delegates to Committers::BufferedCommitter#update_record
|
34
44
|
def update_record(database, table, values, old_key = nil)
|
35
45
|
committer.update_record(database, table, values, old_key)
|
36
46
|
end
|
37
47
|
|
38
|
-
# Delegates to
|
48
|
+
# Delegates to Committers::BufferedCommitter#delete_record
|
39
49
|
def delete_record(database, table, values)
|
40
50
|
committer.delete_record(database, table, values)
|
41
51
|
end
|
@@ -67,6 +77,26 @@ module RR
|
|
67
77
|
committer.finalize(success)
|
68
78
|
end
|
69
79
|
|
80
|
+
# Converts the row values into their proper types as per table definition.
|
81
|
+
# * +table+: name of the table after whose columns is type-casted.
|
82
|
+
# * +row+: A column_name => value hash of the row
|
83
|
+
# Returns a copy of the column_name => value hash (with type-casted values).
|
84
|
+
def type_cast(table, row)
|
85
|
+
@table_columns ||= {}
|
86
|
+
unless @table_columns.include?(table)
|
87
|
+
column_array = session.left.columns(table)
|
88
|
+
column_hash = {}
|
89
|
+
column_array.each {|column| column_hash[column.name] = column}
|
90
|
+
@table_columns[table] = column_hash
|
91
|
+
end
|
92
|
+
columns = @table_columns[table]
|
93
|
+
type_casted_row = {}
|
94
|
+
row.each_pair do |column_name, value|
|
95
|
+
type_casted_row[column_name] = columns[column_name].type_cast(value)
|
96
|
+
end
|
97
|
+
type_casted_row
|
98
|
+
end
|
99
|
+
|
70
100
|
# Logs the outcome of a replication into the replication log table.
|
71
101
|
# * +diff+: the replicated ReplicationDifference
|
72
102
|
# * +outcome+: string summarizing the outcome of the replication
|
@@ -22,6 +22,23 @@ module RR
|
|
22
22
|
Replicators.replicators[session.configuration.options[:replicator]].new(helper)
|
23
23
|
end
|
24
24
|
|
25
|
+
# Calls the event filter for the give difference.
|
26
|
+
# * +diff+: instance of ReplicationDifference
|
27
|
+
# Returns +true+ if replication of the difference should *not* proceed.
|
28
|
+
def event_filtered?(diff)
|
29
|
+
event_filter = helper.options_for_table(diff.changes[:left].table)[:event_filter]
|
30
|
+
if event_filter && event_filter.respond_to?(:before_replicate)
|
31
|
+
not event_filter.before_replicate(
|
32
|
+
diff.changes[:left].table,
|
33
|
+
helper.type_cast(diff.changes[:left].table, diff.changes[:left].key),
|
34
|
+
helper,
|
35
|
+
diff
|
36
|
+
)
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
25
42
|
# Executes the replication run.
|
26
43
|
def run
|
27
44
|
return unless [:left, :right].any? do |database|
|
@@ -53,7 +70,9 @@ module RR
|
|
53
70
|
diff.load
|
54
71
|
break unless diff.loaded?
|
55
72
|
break if sweeper.terminated?
|
56
|
-
|
73
|
+
if diff.type != :no_diff and not event_filtered?(diff)
|
74
|
+
replicator.replicate_difference diff
|
75
|
+
end
|
57
76
|
rescue Exception => e
|
58
77
|
begin
|
59
78
|
helper.log_replication_outcome diff, e.message,
|
@@ -23,7 +23,12 @@ module RR
|
|
23
23
|
@replicators ||= {}
|
24
24
|
@replicators
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
|
+
# Returns the correct replicator class as per provided options hash
|
28
|
+
def self.configured_replicator(options)
|
29
|
+
replicators[options[:replicator]]
|
30
|
+
end
|
31
|
+
|
27
32
|
# Registers one or multiple replicators.
|
28
33
|
# syncer_hash is a Hash with
|
29
34
|
# key:: The adapter symbol as used to reference the replicator
|
@@ -60,13 +60,16 @@ module RR
|
|
60
60
|
# The current ReplicationHelper object
|
61
61
|
attr_accessor :rep_helper
|
62
62
|
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
# Provides default option for the replicator. Optional.
|
64
|
+
# Returns a hash with key => value pairs.
|
65
|
+
def self.default_options
|
66
|
+
{
|
67
|
+
:left_change_handling => :replicate,
|
68
|
+
:right_change_handling => :replicate,
|
69
|
+
:replication_conflict_handling => :ignore,
|
70
|
+
:logged_replication_events => [:ignored_conflicts],
|
71
|
+
}
|
72
|
+
end
|
70
73
|
|
71
74
|
# Checks if an option is configured correctly. Raises an ArgumentError if not.
|
72
75
|
# * +table_spec+: the table specification to which the option belongs. May be +nil+.
|
@@ -173,23 +176,12 @@ module RR
|
|
173
176
|
end
|
174
177
|
end
|
175
178
|
|
176
|
-
# Returns the options for the specified table name.
|
177
|
-
# * +table+: name of the table (left database version)
|
178
|
-
def options_for_table(table)
|
179
|
-
@options_for_table ||= {}
|
180
|
-
unless @options_for_table.include? table
|
181
|
-
@options_for_table[table] = DEFAULT_OPTIONS.merge(
|
182
|
-
rep_helper.session.configuration.options_for_table(table))
|
183
|
-
end
|
184
|
-
@options_for_table[table]
|
185
|
-
end
|
186
|
-
|
187
179
|
# Logs replication of the specified difference as per configured
|
188
180
|
# :+replication_conflict_logging+ / :+left_change_logging+ / :+right_change_logging+ options.
|
189
181
|
# * +winner+: Either the winner database (:+left+ or :+right+) or :+ignore+
|
190
182
|
# * +diff+: the ReplicationDifference instance
|
191
183
|
def log_replication_outcome(winner, diff)
|
192
|
-
options = options_for_table(diff.changes[:left].table)
|
184
|
+
options = rep_helper.options_for_table(diff.changes[:left].table)
|
193
185
|
option_values = [options[:logged_replication_events]].flatten # make sure I have an array
|
194
186
|
if diff.type == :conflict
|
195
187
|
return unless option_values.include?(:all_conflicts) or option_values.include?(:ignored_conflicts)
|
@@ -321,7 +313,7 @@ module RR
|
|
321
313
|
# * :+previous_failure_description+: why the previous replication attempt failed
|
322
314
|
def replicate_difference(diff, remaining_attempts = MAX_REPLICATION_ATTEMPTS, previous_failure_description = nil)
|
323
315
|
raise Exception, previous_failure_description || "max replication attempts exceeded" if remaining_attempts == 0
|
324
|
-
options = options_for_table(diff.changes[:left].table)
|
316
|
+
options = rep_helper.options_for_table(diff.changes[:left].table)
|
325
317
|
if diff.type == :left or diff.type == :right
|
326
318
|
key = diff.type == :left ? :left_change_handling : :right_change_handling
|
327
319
|
option = options[key]
|
data/lib/rubyrep/sync_helper.rb
CHANGED
@@ -26,21 +26,29 @@ module RR
|
|
26
26
|
@tables ||= {:left => left_table, :right => right_table}
|
27
27
|
end
|
28
28
|
|
29
|
+
# Given a column_name => value hash of a full row, returns a
|
30
|
+
# column_name => value hash of the primary key columns.
|
31
|
+
# * +row+: the full row
|
32
|
+
# Returns
|
33
|
+
def extract_key(row)
|
34
|
+
row.reject {|column, value| not primary_key_names.include? column }
|
35
|
+
end
|
36
|
+
|
29
37
|
# Sync options for the current table sync
|
30
38
|
def sync_options; @sync_options ||= table_sync.sync_options; end
|
31
39
|
|
32
|
-
# Delegates to
|
33
|
-
def insert_record(database, values)
|
40
|
+
# Delegates to Committers::BufferedCommitter#insert_record
|
41
|
+
def insert_record(database, table, values)
|
34
42
|
committer.insert_record(database, tables[database], values)
|
35
43
|
end
|
36
44
|
|
37
|
-
# Delegates to
|
38
|
-
def update_record(database, values, old_key = nil)
|
45
|
+
# Delegates to Committers::BufferedCommitter#update_record
|
46
|
+
def update_record(database, table, values, old_key = nil)
|
39
47
|
committer.update_record(database, tables[database], values, old_key)
|
40
48
|
end
|
41
49
|
|
42
|
-
# Delegates to
|
43
|
-
def delete_record(database, values)
|
50
|
+
# Delegates to Committers::BufferedCommitter#delete_record
|
51
|
+
def delete_record(database, table, values)
|
44
52
|
committer.delete_record(database, tables[database], values)
|
45
53
|
end
|
46
54
|
|
@@ -94,15 +94,15 @@ module RR
|
|
94
94
|
case type
|
95
95
|
when source
|
96
96
|
if sync_helper.sync_options[:insert]
|
97
|
-
sync_helper.insert_record target, row
|
97
|
+
sync_helper.insert_record target, sync_helper.tables[target], row
|
98
98
|
end
|
99
99
|
when target
|
100
100
|
if sync_helper.sync_options[:delete]
|
101
|
-
sync_helper.delete_record target, row
|
101
|
+
sync_helper.delete_record target, sync_helper.tables[target], row
|
102
102
|
end
|
103
103
|
when :conflict
|
104
104
|
if sync_helper.sync_options[:update]
|
105
|
-
sync_helper.update_record target, row[source_record_index]
|
105
|
+
sync_helper.update_record target, sync_helper.tables[target], row[source_record_index]
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -148,10 +148,10 @@ module RR
|
|
148
148
|
if option == :ignore
|
149
149
|
# nothing to do
|
150
150
|
elsif option == :delete
|
151
|
-
sync_helper.delete_record type, row
|
151
|
+
sync_helper.delete_record type, sync_helper.tables[type], row
|
152
152
|
elsif option == :insert
|
153
153
|
target = (type == :left ? :right : :left)
|
154
|
-
sync_helper.insert_record target, row
|
154
|
+
sync_helper.insert_record target, sync_helper.tables[target], row
|
155
155
|
else #option must be a Proc
|
156
156
|
option.call sync_helper, type, row
|
157
157
|
end
|
@@ -161,9 +161,9 @@ module RR
|
|
161
161
|
if option == :ignore
|
162
162
|
# nothing to do
|
163
163
|
elsif option == :right_wins
|
164
|
-
sync_helper.update_record :left, row[1]
|
164
|
+
sync_helper.update_record :left, sync_helper.tables[:left], row[1]
|
165
165
|
elsif option == :left_wins
|
166
|
-
sync_helper.update_record :right, row[0]
|
166
|
+
sync_helper.update_record :right, sync_helper.tables[:right], row[0]
|
167
167
|
else #option must be a Proc
|
168
168
|
option.call sync_helper, type, row
|
169
169
|
end
|
data/lib/rubyrep/table_sync.rb
CHANGED
@@ -34,6 +34,26 @@ module RR
|
|
34
34
|
end
|
35
35
|
end
|
36
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
|
+
|
37
57
|
# Executes the table sync. If a block is given, yields each difference with
|
38
58
|
# the following 2 parameters
|
39
59
|
# * +type+
|
@@ -54,7 +74,9 @@ module RR
|
|
54
74
|
|
55
75
|
scan.run do |type, row|
|
56
76
|
yield type, row if block_given? # To enable progress reporting
|
57
|
-
|
77
|
+
unless event_filtered?(type, row)
|
78
|
+
syncer.sync_difference type, row
|
79
|
+
end
|
58
80
|
end
|
59
81
|
|
60
82
|
execute_sync_hook :after_table_sync
|