From 7d1c1efb59b441ab4188f3c42cec7af3209b2485 Mon Sep 17 00:00:00 2001 From: dup2 Date: Mon, 7 Jan 2013 11:19:55 +0100 Subject: [PATCH 1/8] Improved gemspec (homepage, summary) --- rubyrep.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rubyrep.gemspec b/rubyrep.gemspec index a08234af..e282a490 100644 --- a/rubyrep.gemspec +++ b/rubyrep.gemspec @@ -2,13 +2,13 @@ Gem::Specification.new do |s| s.name = %q{rubyrep} - s.version = "1.2.1.2012120701" + s.version = "1.2.1.2013010701" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = [%q{Arndt Lehmann}] s.date = %q{2012-10-09} s.description = %q{A rails based replication engine} - s.homepage = '' + s.homepage = %q{http://http://www.rubyrep.org} s.email = [%q{mail@arndtlehman.com}] s.executables = [%q{rubyrep}] s.extra_rdoc_files = [%q{History.txt}, %q{License.txt}, %q{Manifest.txt}, %q{README.txt}] @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.require_paths = [%q{lib}] s.rubyforge_project = %q{rubyrep} s.rubygems_version = %q{1.8.6} - s.summary = %q{} + s.summary = %q{A solution for asynchronous, master-master replication of relational databases} if s.respond_to? :specification_version then s.specification_version = 3 From cb480a75063e1eb7417143b4889e04127f7884e7 Mon Sep 17 00:00:00 2001 From: dup2 Date: Wed, 9 Jan 2013 11:38:58 +0100 Subject: [PATCH 2/8] Fixed dependency from "= 3.2.8" to ">= 3.2.8" --- rubyrep.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rubyrep.gemspec b/rubyrep.gemspec index e282a490..c4f71aab 100644 --- a/rubyrep.gemspec +++ b/rubyrep.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = %q{rubyrep} - s.version = "1.2.1.2013010701" + s.version = "1.2.1.2013010901" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = [%q{Arndt Lehmann}] @@ -23,8 +23,8 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, ["= 3.2.8"]) - s.add_runtime_dependency(%q, ["= 3.2.8"]) + s.add_runtime_dependency(%q, [">= 3.2.8"]) + s.add_runtime_dependency(%q, [">= 3.2.8"]) s.add_development_dependency(%q, ["~> 2.10"]) else s.add_dependency(%q, ["> 3.0.0"]) @@ -32,8 +32,8 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 2.10"]) end else - s.add_dependency(%q, ["= 3.2.8"]) - s.add_dependency(%q, ["= 3.2.8"]) + s.add_dependency(%q, [">= 3.2.8"]) + s.add_dependency(%q, [">= 3.2.8"]) s.add_dependency(%q, ["~> 2.10"]) end end From 3afe6dd03e80eea7e8e8395f4a95d51f058a8a07 Mon Sep 17 00:00:00 2001 From: Dirk von Gruenigen Date: Wed, 16 Jan 2013 09:09:10 +0100 Subject: [PATCH 3/8] Added a basic logger. --- lib/rubyrep.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/rubyrep.rb b/lib/rubyrep.rb index d6b63b85..c6f6e7fb 100644 --- a/lib/rubyrep.rb +++ b/lib/rubyrep.rb @@ -3,6 +3,7 @@ require 'rubygems' require 'yaml' +require 'logger' gem 'activerecord', '>= 3.0.5' require 'active_record' @@ -55,6 +56,30 @@ require 'generate_runner' require 'noisy_connection' +module RR + # Returns the logger used by RubyRep. It logs to STDOUT by default if + # nothing else is specified via the RR_LOGFILE env variable. The log level + # defaults to INFO, but it can also be set via the RR_LOGLEVEL env variable. + def self.logger + @logger ||= begin + file = (ENV['RR_LOGFILE'] && File.expand_path(ENV['RR_LOGFILE'])) || STDOUT + level = Logger::INFO + + if ENV['RR_LOGLEVEL'] + # Try to get the correct constant from the logger class + env_level = ENV['RR_LOGLEVEL'].upcase + level = Logger.const_get(env_level) if Logger.constants.include?(env_level) + end + + # Create new logger and return it + Logger.new(file).tap do |l| + l.level = level + l.progname = 'RubyRep' + end + end + end +end + Dir["#{File.dirname(__FILE__)}/rubyrep/connection_extenders/*.rb"].each do |extender| # jdbc_extender.rb is only loaded if we are running on jruby require extender unless extender =~ /jdbc/ and not RUBY_PLATFORM =~ /java/ From 99f5433d94eeb4602684d81f62c8387fd3621409 Mon Sep 17 00:00:00 2001 From: Dirk von Gruenigen Date: Wed, 16 Jan 2013 09:13:54 +0100 Subject: [PATCH 4/8] Modified ReplicationRunner to try to automatically reconnect if a db connection is lost. Also added some logging by the way. Fixes issue #1. --- lib/rubyrep/replication_runner.rb | 38 +++++++++++++++++++++++-------- lib/rubyrep/session.rb | 38 +++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/lib/rubyrep/replication_runner.rb b/lib/rubyrep/replication_runner.rb index 8608406b..71122c4a 100644 --- a/lib/rubyrep/replication_runner.rb +++ b/lib/rubyrep/replication_runner.rb @@ -71,11 +71,15 @@ def process_options(args) # Loads config file and creates session if necessary. def session unless @session + RR.logger.debug 'RUNNER - No session exists, creating one now' + unless @config load options[:config_file] @config = Initializer.configuration end @session = Session.new @config + + RR.logger.debug 'RUNNER - Successfully created session' end @session end @@ -89,7 +93,7 @@ def clear_session def pause_replication @last_run ||= 1.year.ago now = Time.now - @next_run = @last_run + session.configuration.options[:replication_interval] + @next_run = @last_run + Initializer.configuration.options[:replication_interval] unless now >= @next_run waiting_time = @next_run - now @waiter_thread.join waiting_time @@ -122,10 +126,12 @@ def execute_once run = ReplicationRun.new session, sweeper run.run end.terminated? - raise "replication run timed out" if terminated - rescue Exception => e - clear_session - raise e + + if terminated + message = 'RUNNER - Replication run timed out' + RR.logger.error(message) + raise message + end end # Executes an endless loop of replication runs @@ -135,13 +141,25 @@ def execute until termination_requested do begin + RR.logger.debug 'RUNNER - Starting replication cycle' execute_once + if !@last_run_successfull && !@last_run_successfull.nil? + RR.logger.info 'RUNNER - Connection successfully established again' + end + RR.logger.debug 'RUNNER - Finished replication cycle successfully' + @last_run_successfull = true rescue Exception => e - now = Time.now.iso8601 - $stderr.puts "#{now} Exception caught: #{e}" - if @last_exception_message != e.to_s # only print backtrace if something changed - @last_exception_message = e.to_s - $stderr.puts e.backtrace.map {|line| line.gsub(/^/, "#{' ' * now.length} ")} + # Check if it's a connection problem + if (e.is_a?(PG::Error) && e.to_s =~ %r/could not connect/) || e.to_s =~ %r/no connection to '(.*)' databases/ + if @last_run_successfull + RR.logger.error 'RUNNER - Lost connection to one database, terminating session.' + @last_run_successfull = false + end + + clear_session + RR.logger.debug 'RUNNER - Databases disconnected, a new one will be built when needed...' + else + RR.logger.error("Exception caught: #{e}") end end pause_replication diff --git a/lib/rubyrep/session.rb b/lib/rubyrep/session.rb index 0428a2d9..9d61d3d3 100644 --- a/lib/rubyrep/session.rb +++ b/lib/rubyrep/session.rb @@ -123,6 +123,8 @@ def sort_table_pairs(table_pairs) # * +database+: target database (either +:left+ or :+right+) def database_unreachable?(database) unreachable = true + RR.logger.debug "SESSION - Checking if database '#{database}' is unreachable" + Thread.new do begin if send(database) && send(database).select_one("select 1+1 as x")['x'].to_i == 2 @@ -130,6 +132,8 @@ def database_unreachable?(database) end end rescue nil end.join configuration.options[:database_connection_timeout] + + RR.logger.debug "SESSION - Database '#{database}' is #{unreachable ? 'unreachable' : 'reachable'}" unreachable end @@ -143,19 +147,23 @@ def disconnect_databases # Disconnnects the specified database # * +database+: the target database (either :+left+ or :+right+) def disconnect_database(database) + RR.logger.debug "SESSION - Trying to disconnect from database '#{database}'" + proxy, connection = @proxies[database], @connections[database] @proxies[database] = nil @connections[database] = nil if proxy proxy.destroy_session(connection) end + + RR.logger.debug "SESSION - Database '#{database}' successfully disconnected" end # Refreshes both database connections # * +options+: A options hash with the following settings # * :+forced+: if +true+, always establish a new database connection def refresh(options = {}) - [:left, :right].each {|database| refresh_database_connection database, options} + [:left, :right].each { |database| refresh_database_connection(database, options) } end # Refreshes the specified database connection. @@ -172,17 +180,27 @@ def refresh_database_connection(database, options) end.join configuration.options[:database_connection_timeout] end - connect_exception = nil # step 2: try to reconnect the database - Thread.new do - begin - connect_database database - rescue Exception => e - # save exception so it can be rethrown outside of the thread - connect_exception = e + begin + connect_exception = nil + RR.logger.debug "SESSION - Trying to reconnect to database '#{database}'" + + Thread.new do + begin + connect_database database + rescue Exception => e + # save exception so it can be rethrown outside of the thread + connect_exception = e + end + end.join configuration.options[:database_connection_timeout] + + if connect_exception + RR.logger.debug "SESSION - Failed to established connection to database '#{database}'" + raise connect_exception + else + RR.logger.debug "SESSION - Successfully established connection to database '#{database}'" end - end.join configuration.options[:database_connection_timeout] - raise connect_exception if connect_exception + end # step 3: verify if database connections actually work (to detect silent connection failures) if database_unreachable?(database) From 4b3d77a9b9131ae9ff11d2a6203f71cd36a286ff Mon Sep 17 00:00:00 2001 From: dup2 Date: Tue, 28 May 2013 23:33:44 +0200 Subject: [PATCH 5/8] Fixed loglevel constant lookup Logger.constants only returns symbols, not strings --- lib/rubyrep.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubyrep.rb b/lib/rubyrep.rb index c6f6e7fb..336003a4 100644 --- a/lib/rubyrep.rb +++ b/lib/rubyrep.rb @@ -68,7 +68,7 @@ def self.logger if ENV['RR_LOGLEVEL'] # Try to get the correct constant from the logger class env_level = ENV['RR_LOGLEVEL'].upcase - level = Logger.const_get(env_level) if Logger.constants.include?(env_level) + level = Logger.const_get(env_level) if Logger.constants.include?(env_level.to_sym) end # Create new logger and return it From 47337f1312bddc238ee5f46eac42c90d4a0d70c0 Mon Sep 17 00:00:00 2001 From: "Dr. Zarkov" Date: Fri, 5 Jul 2013 10:53:15 +0200 Subject: [PATCH 6/8] Fixed dependency to us a pessimistic constraint rubyrep is not compatible with ActiveSupport/ActiveRecord 4.0 --- rubyrep.gemspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rubyrep.gemspec b/rubyrep.gemspec index c4f71aab..0432d41d 100644 --- a/rubyrep.gemspec +++ b/rubyrep.gemspec @@ -23,8 +23,8 @@ Gem::Specification.new do |s| s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 3.2.8"]) - s.add_runtime_dependency(%q, [">= 3.2.8"]) + s.add_runtime_dependency(%q, ["~> 3.2.8"]) + s.add_runtime_dependency(%q, ["~> 3.2.8"]) s.add_development_dependency(%q, ["~> 2.10"]) else s.add_dependency(%q, ["> 3.0.0"]) @@ -32,8 +32,8 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 2.10"]) end else - s.add_dependency(%q, [">= 3.2.8"]) - s.add_dependency(%q, [">= 3.2.8"]) + s.add_dependency(%q, ["~> 3.2.8"]) + s.add_dependency(%q, ["~> 3.2.8"]) s.add_dependency(%q, ["~> 2.10"]) end end From 6b1b38451a1fdbe3c6f9b093b4dddf37b84c6deb Mon Sep 17 00:00:00 2001 From: "Dr. Zarkov" Date: Fri, 5 Jul 2013 14:25:33 +0200 Subject: [PATCH 7/8] Fixed another dependency (which is not normally used in this version) --- rubyrep.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rubyrep.gemspec b/rubyrep.gemspec index 0432d41d..5fac8263 100644 --- a/rubyrep.gemspec +++ b/rubyrep.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |s| s.add_runtime_dependency(%q, ["~> 3.2.8"]) s.add_development_dependency(%q, ["~> 2.10"]) else - s.add_dependency(%q, ["> 3.0.0"]) - s.add_dependency(%q, ["> 3.0.0"]) + s.add_dependency(%q, ["~> 3.2.8"]) + s.add_dependency(%q, ["~> 3.2.8"]) s.add_dependency(%q, ["~> 2.10"]) end else From 701ee0239d7a5dc08ff7cbb1d3ca7c60ba91c7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20von=20Gru=CC=88nigen?= Date: Thu, 29 Aug 2013 15:59:00 +0200 Subject: [PATCH 8/8] Correct regex which is used to detect if it's a connection problem --- lib/rubyrep/replication_runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rubyrep/replication_runner.rb b/lib/rubyrep/replication_runner.rb index 71122c4a..b44188ca 100644 --- a/lib/rubyrep/replication_runner.rb +++ b/lib/rubyrep/replication_runner.rb @@ -150,7 +150,7 @@ def execute @last_run_successfull = true rescue Exception => e # Check if it's a connection problem - if (e.is_a?(PG::Error) && e.to_s =~ %r/could not connect/) || e.to_s =~ %r/no connection to '(.*)' databases/ + if (e.is_a?(PG::Error) && e.to_s =~ %r/could not connect/) || e.to_s =~ %r/no connection to '(.*)' database/ if @last_run_successfull RR.logger.error 'RUNNER - Lost connection to one database, terminating session.' @last_run_successfull = false