mysql-heartbeat 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e93ade565eea9f1b66486408985bfb44d5b79ac
4
+ data.tar.gz: 9646d80688a80acbceabac57b5b50aa596fa878e
5
+ SHA512:
6
+ metadata.gz: ed5e41132f88501024905824e6cf6ff5a99e55c592d38f1170658c74a7bfce9b00ca8ec8f72b0f8fa4914fbada56403cd3faf6aa60845c8e7587e1a0b7c25311
7
+ data.tar.gz: 3efd5f6234c8b32451821f4d23017b8dbb052b1085dcc5d26f5f47d6d82cd8b180d18a6fbbbfc3ebae86539ec12ed43fd1464cdff28860d7ed89ffb82ba8237f
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ bundler_args: ""
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
6
+ env:
7
+ - "RACK_ENV=test"
8
+ script:
9
+ - bundle exec rspec --color --format progress
10
+ - bundle exec rubocop
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source 'http://rubygems.org'
2
+ gem 'sinatra'
3
+ gem 'mysql2'
4
+ gem 'celluloid'
5
+ gem 'thin'
6
+
7
+ group :test, :development do
8
+ gem 'rspec'
9
+ end
10
+
11
+ group :development do
12
+ gem 'guard'
13
+ gem 'guard-rubocop'
14
+ gem 'guard-rspec'
15
+ end
16
+
17
+ group :test do
18
+ gem 'rubocop'
19
+ gem 'rack-test'
20
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,87 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ ast (1.1.0)
5
+ celluloid (0.15.2)
6
+ timers (~> 1.1.0)
7
+ coderay (1.0.9)
8
+ daemons (1.1.9)
9
+ diff-lcs (1.2.4)
10
+ eventmachine (1.0.3)
11
+ ffi (1.9.3)
12
+ formatador (0.2.4)
13
+ guard (2.2.2)
14
+ formatador (>= 0.2.4)
15
+ listen (~> 2.1)
16
+ lumberjack (~> 1.0)
17
+ pry (>= 0.9.12)
18
+ thor (>= 0.18.1)
19
+ guard-rspec (4.0.3)
20
+ guard (>= 2.1.1)
21
+ rspec (~> 2.14)
22
+ guard-rubocop (1.0.0)
23
+ guard (~> 2.0)
24
+ rubocop (~> 0.10)
25
+ listen (2.2.0)
26
+ celluloid (>= 0.15.2)
27
+ rb-fsevent (>= 0.9.3)
28
+ rb-inotify (>= 0.9)
29
+ lumberjack (1.0.4)
30
+ method_source (0.8.2)
31
+ mysql2 (0.3.13)
32
+ parser (2.0.0)
33
+ ast (~> 1.1)
34
+ slop (~> 3.4, >= 3.4.5)
35
+ powerpack (0.0.9)
36
+ pry (0.9.12.2)
37
+ coderay (~> 1.0.5)
38
+ method_source (~> 0.8)
39
+ slop (~> 3.4)
40
+ rack (1.5.2)
41
+ rack-protection (1.5.1)
42
+ rack
43
+ rack-test (0.6.2)
44
+ rack (>= 1.0)
45
+ rainbow (1.1.4)
46
+ rb-fsevent (0.9.3)
47
+ rb-inotify (0.9.2)
48
+ ffi (>= 0.5.0)
49
+ rspec (2.14.1)
50
+ rspec-core (~> 2.14.0)
51
+ rspec-expectations (~> 2.14.0)
52
+ rspec-mocks (~> 2.14.0)
53
+ rspec-core (2.14.7)
54
+ rspec-expectations (2.14.3)
55
+ diff-lcs (>= 1.1.3, < 2.0)
56
+ rspec-mocks (2.14.4)
57
+ rubocop (0.14.1)
58
+ parser (~> 2.0)
59
+ powerpack (~> 0.0.6)
60
+ rainbow (>= 1.1.4)
61
+ sinatra (1.4.4)
62
+ rack (~> 1.4)
63
+ rack-protection (~> 1.4)
64
+ tilt (~> 1.3, >= 1.3.4)
65
+ slop (3.4.6)
66
+ thin (1.6.1)
67
+ daemons (>= 1.0.9)
68
+ eventmachine (>= 1.0.0)
69
+ rack (>= 1.0.0)
70
+ thor (0.18.1)
71
+ tilt (1.4.1)
72
+ timers (1.1.0)
73
+
74
+ PLATFORMS
75
+ ruby
76
+
77
+ DEPENDENCIES
78
+ celluloid
79
+ guard
80
+ guard-rspec
81
+ guard-rubocop
82
+ mysql2
83
+ rack-test
84
+ rspec
85
+ rubocop
86
+ sinatra
87
+ thin
data/Guardfile ADDED
@@ -0,0 +1,29 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rubocop do
5
+ watch(%r{.+\.rb$})
6
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
7
+ end
8
+
9
+ guard :rspec do
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
12
+ watch('spec/spec_helper.rb') { "spec" }
13
+
14
+ # Rails example
15
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
16
+ watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
17
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
18
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
19
+ watch('config/routes.rb') { "spec/routing" }
20
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
21
+
22
+ # Capybara features specs
23
+ watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
24
+
25
+ # Turnip features and steps
26
+ watch(%r{^spec/acceptance/(.+)\.feature$})
27
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
28
+ end
29
+
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ [![Dependency Status](https://gemnasium.com/johnbarney/mysql-heartbeat.png)](https://gemnasium.com/johnbarney/mysql-heartbeat)
2
+ [![Build Status](https://secure.travis-ci.org/johnbarney/mysql-heartbeat.png)](http://travis-ci.org/johnbarney/mysql-heartbeat)
3
+ [![Gem Version](https://badge.fury.io/rb/mysql-heartbeat.png)](http://badge.fury.io/rb/mysql-heartbeat)
4
+ # MySQL Heartbeat
5
+
6
+ A health monitor for MySQL replication. This can be used in tandem with applications like HAProxy to check the health of replication, and load balance across read slaves.
7
+
8
+ Installation
9
+ ------------
10
+
11
+ Installation is as easy as installing a gem.
12
+
13
+ $ gem install mysql-heartbeat
14
+ $ mysql-heartbeat yourconfig.yml
15
+
16
+ Configuration
17
+ -------------
18
+
19
+ Make a copy of database.yml for each MySQL instance you want to watch. You can launch as many of these instances as you like as long as they each use a different local port.
20
+
21
+ custom.yml:
22
+
23
+ host: "localhost" # Address of MySQL instance
24
+ username: "health" # Username of MySQL user
25
+ password: "health99" # Password of MySQL user
26
+ health_port: 6977 # Port this instance of MySQL health resides on
27
+ tolerance: 5 # How far behind MySQL replication can be before healthchecks fail
28
+
29
+ $ ruby heartbeat.rb custom.yml
30
+
31
+ Note: The user must have REPLICATION CLIENT privileges for mysql_heartbeat to function.
32
+
33
+ Example:
34
+
35
+ mysql> create user 'health'@'localhost' identified by 'health99';
36
+
37
+ mysql> grant REPLICATION CLIENT on *.* to 'health'@'localhost';
38
+
39
+ Process Control
40
+ ---------------
41
+
42
+ I would recommend either [Supervisord](http://supervisord.org/ "Supervisord") or [Bluepill](https://github.com/bluepill-rb/bluepill "Bluepill") to manage forking and management of instances.
43
+
44
+ HAProxy Configuration
45
+ ---------------------
46
+
47
+ Coming Soon!
48
+
49
+ Work in Progress
50
+ ----------------
51
+
52
+ This project is primarily to teach myself how to write tests, and integrate with Travis. It's still a long way from complete. Contributions and suggestions are always welcome.
53
+
54
+ Contributing
55
+ ------------
56
+
57
+ See GitHub's ["Fork A Repo"](https://help.github.com/articles/fork-a-repo "Forking a project") for more information.
@@ -0,0 +1,15 @@
1
+ #! /usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ if RUBY_VERSION >= '1.9.3'
5
+ if ARGV[0].nil? && ENV['RACK_ENV'] != 'test'
6
+ puts 'No configuration file specified'
7
+ exit 1
8
+ end
9
+ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
10
+ require 'mysql-heartbeat'
11
+ HeartBeat.run!
12
+ else
13
+ puts 'mysql-heartbeat supports only Ruby 1.9.3+'
14
+ exit(-1)
15
+ end
@@ -0,0 +1,5 @@
1
+ host: "localhost"
2
+ username: "health"
3
+ password: "check99"
4
+ tolerance: 5
5
+ health_port: 6977
data/config/travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ host: 'localhost'
2
+ username: 'travis'
3
+ password: ''
4
+ tolerance: 5
5
+ health_port: 6977
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'sinatra/base'
3
+ require 'yaml'
4
+ require 'rack/test' if ENV['RACK_ENV'] == 'test'
5
+ require 'mysql-heartbeat/health'
6
+ require 'mysql-heartbeat/worker'
7
+ require 'mysql-heartbeat/version'
8
+
9
+ # Heartbeat Class
10
+ class HeartBeat < Sinatra::Base
11
+ configure do
12
+ if ENV['RACK_ENV'] == 'test'
13
+ config = YAML.load_file("#{File.dirname(__FILE__)}/../config/travis.yml")
14
+ else
15
+ config = YAML.load_file(ARGV[0])
16
+ end
17
+ set :port, config['health_port'].to_i
18
+ set :bind, '0.0.0.0'
19
+ set :server, 'thin'
20
+ set :process, HeartbeatWorker.new(config)
21
+ end
22
+
23
+ options '/' do
24
+ settings.process.current_health
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'mysql2'
3
+
4
+ # Is MySQL healthy?
5
+ class Health
6
+ def initialize(config)
7
+ @conn = Mysql2::Client.new(host: config['host'],
8
+ username: config['username'],
9
+ password: config['password']
10
+ )
11
+ @tolerance = config[:tolerance]
12
+ get_status
13
+ rescue => ex
14
+ puts ex
15
+ puts 'Could not connect to MySQL.'
16
+ end
17
+
18
+ def get_status
19
+ results = @conn.query('show slave status;')
20
+ results.each do |row|
21
+ @sec_behind = row['Seconds_Behind_Master'].to_i
22
+ @slave_sql = row['Slave_SQL_Running'] == 'Yes'
23
+ @slave_io = row['Slave_IO_Running'] == 'Yes'
24
+ @last_error = row['Last_Error']
25
+ end
26
+ rescue => ex
27
+ puts ex
28
+ puts 'Could not query slave status.'
29
+ end
30
+
31
+ def replicating?
32
+ @slave_sql && @slave_io
33
+ end
34
+
35
+ def behind?
36
+ @sec_behind > @tolerance unless @sec_behind.nil?
37
+ end
38
+
39
+ def healthy?
40
+ if replicating? && !behind?
41
+ return [200, "OK\n"]
42
+ else
43
+ reason = "Replication is not healthy!
44
+ \nSlave_IO running: #{@slave_io}
45
+ \nSlave_SQL running: #{@slave_sql}
46
+ \nSeconds Behind: #{@sec_behind}
47
+ \nLast Error: #{@last_error}"
48
+ return [503, reason]
49
+ end
50
+ end
51
+
52
+ def conn_close
53
+ @conn.close unless @conn.nil?
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # Version
3
+ module MysqlHeartBeat
4
+ VERSION = '0.1.1'.freeze
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'celluloid'
3
+
4
+ # Celluloid worker
5
+ class HeartbeatWorker
6
+ include Celluloid
7
+ attr_reader :current_health
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ async.worker
12
+ end
13
+
14
+ def worker
15
+ loop do
16
+ begin
17
+ db = Health.new(@config)
18
+ @current_health = db.healthy?
19
+ sleep 15
20
+ ensure
21
+ db.conn_close unless db.nil?
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
2
+
3
+ require "mysql-heartbeat/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'mysql-heartbeat'
7
+ s.version = MysqlHeartBeat::VERSION.dup
8
+ s.date = '2013-10-28'
9
+ s.summary = "Queries the replication health of a MySQL server."
10
+ s.description = <<-EOF
11
+ Queries the replication health of a MySQL server.
12
+ A health monitor for MySQL replication. This can be used
13
+ in tandem with applications like HAProxy to check the
14
+ health of replication, and load balance across read slaves.
15
+ EOF
16
+
17
+ s.add_dependency 'sinatra', '~> 1.4'
18
+ s.add_dependency 'mysql2', '~> 0.3'
19
+ s.add_dependency 'celluloid', '~> 0.15'
20
+ s.add_dependency 'thin', '~> 1.6'
21
+
22
+ s.authors = ["John Barney"]
23
+ s.email = 'johnb0011@gmail.com'
24
+ s.required_ruby_version = '>= 1.9.3'
25
+ s.platform = Gem::Platform::RUBY
26
+ s.files = `git ls-files`.split("\n")
27
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
+ s.homepage = 'http://github.com/johnb0011/mysql_heartbeat'
29
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # spec/features/base_spec.rb
3
+ require_relative '../spec_helper'
4
+
5
+ describe 'Root Path' do
6
+ describe 'OPTIONS /' do
7
+ before { options '/' }
8
+
9
+ it 'is successful' do
10
+ # Yep. I wrote a test to pass on a 503. Deal with it. (This will change)
11
+ expect(last_response.status).to eq(503)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # spec/spec_helper.rb
3
+ ENV['RACK_ENV'] = 'test'
4
+
5
+ require_relative File.join('..', 'lib/mysql-heartbeat')
6
+
7
+ RSpec.configure do |config|
8
+ include Rack::Test::Methods
9
+
10
+ def app
11
+ HeartBeat
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysql-heartbeat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - John Barney
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mysql2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: celluloid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.15'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.15'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thin
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ description: " Queries the replication health of a MySQL server.\n A health
70
+ monitor for MySQL replication. This can be used \n in tandem with applications
71
+ like HAProxy to check the\n health of replication, and load balance across read
72
+ slaves.\n"
73
+ email: johnb0011@gmail.com
74
+ executables:
75
+ - mysql-heartbeat
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - .travis.yml
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - Guardfile
83
+ - README.md
84
+ - bin/mysql-heartbeat
85
+ - config/example_mysql.yml
86
+ - config/travis.yml
87
+ - lib/mysql-heartbeat.rb
88
+ - lib/mysql-heartbeat/health.rb
89
+ - lib/mysql-heartbeat/version.rb
90
+ - lib/mysql-heartbeat/worker.rb
91
+ - mysql-heartbeat.gemspec
92
+ - spec/requests/base_spec.rb
93
+ - spec/spec_helper.rb
94
+ homepage: http://github.com/johnb0011/mysql_heartbeat
95
+ licenses: []
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: 1.9.3
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.0.3
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Queries the replication health of a MySQL server.
117
+ test_files: []