rubyrep 1.0.9 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|