theworkinggroup-mysql-replication-helper 0.1.0 → 0.2.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.
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 1
3
+ :minor: 2
4
4
  :patch: 0
@@ -1,6 +1,10 @@
1
+ require 'rubygems'
2
+ require 'mysql'
3
+
1
4
  module MysqlReplicationHelper
2
5
  # == Autoloads ============================================================
3
6
 
7
+ autoload(:Agent, 'mysql_replication_helper/agent')
4
8
  autoload(:ErrorHandler, 'mysql_replication_helper/error_handler')
5
9
  autoload(:Daemon, 'mysql_replication_helper/daemon')
6
10
  end
@@ -0,0 +1,51 @@
1
+ module MysqlReplicationHelper
2
+ class Agent
3
+ autoload(:Master, 'mysql_replication_helper/agent/master')
4
+ autoload(:Slave, 'mysql_replication_helper/agent/slave')
5
+
6
+ DEFAULT_OPTIONS = {
7
+ :host => 'localhost',
8
+ :user => 'root',
9
+ :master_socket => '/local/db/mysql.sock',
10
+ :slave_socket => '/ebs/db/mysql.sock'
11
+ }
12
+
13
+ def initialize(options)
14
+ @options = with_default_options(options)
15
+ end
16
+
17
+ def connection
18
+ @connection ||=
19
+ Mysql.real_connect(
20
+ @options[:host],
21
+ user_name,
22
+ @options[:password],
23
+ @options[:db],
24
+ @options[:port],
25
+ socket_name
26
+ )
27
+ end
28
+
29
+ def user_name
30
+ @options[:user]
31
+ end
32
+
33
+ def socket_name
34
+ @options[:socket]
35
+ end
36
+
37
+ def with_default_options(options)
38
+ DEFAULT_OPTIONS.merge(options)
39
+ end
40
+
41
+ def execute(statement)
42
+ STDERR.puts(statement)
43
+ connection.real_query(statement)
44
+ end
45
+
46
+ def query(statement)
47
+ STDERR.puts(statement)
48
+ connection.query(statement)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,28 @@
1
+ module MysqlReplicationHelper
2
+ class Agent
3
+ class Master < Agent
4
+ def poll!
5
+ # Nothing yet
6
+ end
7
+
8
+ def master_status
9
+ row = query("SHOW MASTER STATUS").fetch_row
10
+
11
+ return unless (row)
12
+
13
+ {
14
+ :master_log_file => row[0],
15
+ :master_log_position => row[1].to_i
16
+ }
17
+ end
18
+
19
+ def user_name
20
+ @options[:master_user] or super
21
+ end
22
+
23
+ def socket_name
24
+ @options[:master_socket] or super
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ module MysqlReplicationHelper
2
+ class Agent
3
+ class Slave < Agent
4
+ include MysqlReplicationHelper::ErrorHandler
5
+
6
+ def poll!
7
+ if (configured?)
8
+ if (error_message = slave_error)
9
+ if (statements = sql_to_recover_from(error_message))
10
+ statements.each do |sql|
11
+ connection.real_query(sql)
12
+ end
13
+ else
14
+ # Unrecoverable error?
15
+ end
16
+ end
17
+ else
18
+ assign_master(@options[:master])
19
+ start!
20
+ end
21
+ end
22
+
23
+ def configured?
24
+ !!query("SHOW SLAVE STATUS").fetch_row
25
+ end
26
+
27
+ def slave_error
28
+ row = query("SHOW SLAVE STATUS").fetch_row
29
+
30
+ row and row[19]
31
+ end
32
+
33
+ def assign_master(master)
34
+ master_status = master.master_status
35
+
36
+ master_options =
37
+ {
38
+ 'MASTER_HOST' => 'localhost',
39
+ 'MASTER_USER' => master.user_name,
40
+ 'MASTER_PORT' => 3306,
41
+ 'MASTER_LOG_FILE' => master_status[:master_log_file],
42
+ 'MASTER_LOG_POS' => master_status[:master_log_position]
43
+ }.collect do |k, v|
44
+ case (v)
45
+ when String:
46
+ "#{k}='#{Mysql.quote(v)}'"
47
+ else
48
+ "#{k}=#{v}"
49
+ end
50
+ end
51
+
52
+ execute("CHANGE MASTER TO #{master_options * ', '}")
53
+ end
54
+
55
+ def start!
56
+ execute("START SLAVE")
57
+ end
58
+
59
+ def user_name
60
+ @options[:slave_user] or super
61
+ end
62
+
63
+ def socket_name
64
+ @options[:slave_socket] or super
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,18 @@
1
+ module MysqlReplicationHelper
2
+ class Daemon
3
+ def initialize(options)
4
+ @options = options
5
+
6
+ @options[:master] = @master = Agent::Master.new(options)
7
+ @options[:slave] = @slave = Agent::Slave.new(options)
8
+ end
9
+
10
+ def run!
11
+ while (true)
12
+ @master.poll!
13
+ @slave.poll!
14
+ sleep(10)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Daemonize module
4
+ #
5
+
6
+ require 'rubygems'
7
+ require 'yaml'
8
+ require 'optparse'
9
+
10
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
11
+
12
+ require 'mysql_replication_helper'
13
+
14
+ # == Constants ==============================================================
15
+
16
+ CONFIG_FILE_LOCATIONS = [
17
+ "/etc/replication-helper.conf",
18
+ "/etc/replication-helper/config",
19
+ "~/.replication-helper/config"
20
+ ].collect { |p| File.expand_path(p) }.freeze
21
+
22
+ DEFAULT_CONFIG = {
23
+ }
24
+
25
+ # == Options ================================================================
26
+
27
+ op = OptionParser.new
28
+ options = { }
29
+ config = { }
30
+ config_file = nil
31
+
32
+ op.on("--master-socket=s") { |socket| options[:master_socket] = socket }
33
+ op.on("--master-data=s") { |dir| options[:master_data] = dir }
34
+ op.on("--master-user=s") { |name| options[:master_user] = name }
35
+
36
+ op.on("--slave-socket=s") { |socket| options[:slave_socket] = socket }
37
+ op.on("--slave-data=s") { |dir| options[:slave_data] = dir }
38
+ op.on("--slave-user=s") { |name| options[:slave_user] = name }
39
+
40
+ op.on("-c", "--config=s") { |path| config_file = path }
41
+ op.on("-v", "--verbose") { options[:verbose] = true }
42
+ op.on("-d", "--daemon") { options[:daemon] = true }
43
+ op.on("-h", "--help") { show_help }
44
+
45
+ args = op.parse(*ARGV)
46
+
47
+ # == Configuration ==========================================================
48
+
49
+ if (config_file)
50
+ if (File.exist?(config_file))
51
+ config = YAML.load(open(config_file))
52
+ end
53
+ end
54
+
55
+ config = DEFAULT_CONFIG.merge(config).merge(options)
56
+
57
+ # == Main ===================================================================
58
+
59
+ MysqlReplicationHelper::Daemon.new(config).run!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theworkinggroup-mysql-replication-helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Tadman
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-24 00:00:00 -07:00
12
+ date: 2009-04-26 00:00:00 -07:00
13
13
  default_executable: replication-helper
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: daemons
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
16
25
  description:
17
26
  email: github@tadman.ca
18
27
  executables:
@@ -27,6 +36,11 @@ files:
27
36
  - VERSION.yml
28
37
  - bin/replication-helper
29
38
  - lib/mysql_replication_helper.rb
39
+ - lib/mysql_replication_helper/agent.rb
40
+ - lib/mysql_replication_helper/agent/master.rb
41
+ - lib/mysql_replication_helper/agent/slave.rb
42
+ - lib/mysql_replication_helper/daemon.rb
43
+ - lib/mysql_replication_helper/daemon_launcher.rb
30
44
  - lib/mysql_replication_helper/error_handler.rb
31
45
  - test/mysql_replication_helper/error_handler_test.rb
32
46
  - test/mysql_replication_helper_test.rb
@@ -55,7 +69,7 @@ requirements: []
55
69
  rubyforge_project:
56
70
  rubygems_version: 1.2.0
57
71
  signing_key:
58
- specification_version: 3
72
+ specification_version: 2
59
73
  summary: MySQL Replication Helper
60
74
  test_files:
61
75
  - test/mysql_replication_helper/error_handler_test.rb