lib/rubyrep/replication_run.rb in rubyrep-1.1.2 vs lib/rubyrep/replication_run.rb in rubyrep-1.2.0
- old
+ new
@@ -9,10 +9,15 @@
attr_accessor :session
# The current TaskSweeper
attr_accessor :sweeper
+ # An array of ReplicationDifference which originally failed replication but should be tried one more time
+ def second_chancers
+ @second_chancers ||= []
+ end
+
# Returns the current ReplicationHelper; creates it if necessary
def helper
@helper ||= ReplicationHelper.new(self)
end
@@ -37,10 +42,24 @@
else
false
end
end
+ # Returns the next available ReplicationDifference.
+ # (Either new unprocessed differences or if not available, the first available 'second chancer'.)
+ #
+ def load_difference
+ @loaders ||= LoggedChangeLoaders.new(session)
+ @loaders.update # ensure the cache of change log records is up-to-date
+ diff = ReplicationDifference.new @loaders
+ diff.load
+ unless diff.loaded? or second_chancers.empty?
+ diff = second_chancers.shift
+ end
+ diff
+ end
+
# Executes the replication run.
def run
return unless [:left, :right].any? do |database|
changes_pending = false
t = Thread.new do
@@ -55,32 +74,39 @@
# Apparently sometimes above check for changes takes already so long, that
# the replication run times out.
# Check for this and if timed out, return (silently).
return if sweeper.terminated?
- loaders = LoggedChangeLoaders.new(session)
-
success = false
begin
replicator # ensure that replicator is created and has chance to validate settings
loop do
begin
- loaders.update # ensure the cache of change log records is up-to-date
- diff = ReplicationDifference.new loaders
- diff.load
+ diff = load_difference
break unless diff.loaded?
break if sweeper.terminated?
if diff.type != :no_diff and not event_filtered?(diff)
replicator.replicate_difference diff
end
rescue Exception => e
- begin
- helper.log_replication_outcome diff, e.message,
- e.class.to_s + "\n" + e.backtrace.join("\n")
- rescue Exception => _
- # if logging to database itself fails, re-raise the original exception
- raise e
+ if e.message =~ /violates foreign key constraint|foreign key constraint fails/i and !diff.second_chance?
+ # Note:
+ # Identifying the foreign key constraint violation via regular expression is
+ # database dependent and *dirty*.
+ # It would be better to use the ActiveRecord #translate_exception mechanism.
+ # However as per version 3.0.5 this doesn't work yet properly.
+
+ diff.second_chance = true
+ second_chancers << diff
+ else
+ begin
+ helper.log_replication_outcome diff, e.message,
+ e.class.to_s + "\n" + e.backtrace.join("\n")
+ rescue Exception => _
+ # if logging to database itself fails, re-raise the original exception
+ raise e
+ end
end
end
end
success = true
ensure