rails_health_checks 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 128aa6485e85de5719cfb25c05192f5dd0277f8277bc6c8b4f74a095402f5f4b
4
- data.tar.gz: e3b5eb28dd364012552e7f91e3e3b01f9d1db3778b4f7d0fe88662aed45c4073
3
+ metadata.gz: d0f595984abf8a7db84183766d61918bdf684642b6ea67b2075afbda8f4cbae6
4
+ data.tar.gz: ab35a0deb32d298eae8dadc04c39dbf8ca34b8e77018625ebad8ba81e1901f34
5
5
  SHA512:
6
- metadata.gz: 02001fd4bbb80bc41567c449c4653aef0afa72766b1413cabe9cbbe61a248d2ef6c86bc1ae18bd2fad378b46748b9550e0b74001026514233de55aa04cc4f6ea
7
- data.tar.gz: 34460c74d90057174f63461569e2132e320ec4440141e7914fa9c82075b6268f46680514f667dfe9a8eb6932964ab30ffa1d80f071bf8f22e15f9e8a5f6aaa0b
6
+ metadata.gz: 2f2db831794c15dbf59c6763ed65a092e9bc56db1dc87e35d4565cccd63303cfe9555a7b36f738e6a861086c0362f8eb98459e99ea46f5838bfeb3862130ebf5
7
+ data.tar.gz: 3d5b057c32506961cf0416d37ea45c70f5218767961ddeed208b37887abd7781187fc1f480aff2e5fb522bfad8400e355fa745a04ec876a064e1aeb938e91c78
data/README.md CHANGED
@@ -76,7 +76,7 @@ Status values: `ok` | `degraded` | `critical`. Overall status is `critical` if a
76
76
  ```ruby
77
77
  # config/initializers/rails_health_checks.rb
78
78
  RailsHealthChecks.configure do |config|
79
- config.checks = [:database] # checks to run (default: [:database])
79
+ config.checks = [:database, :cache] # checks to run (default: [:database])
80
80
  config.timeout = 5 # global timeout per check in seconds (default: 5)
81
81
  end
82
82
  ```
@@ -126,6 +126,14 @@ The block receives the `ActionDispatch::Request` object and must return a truthy
126
126
  | Check | Description |
127
127
  |-------|-------------|
128
128
  | `:database` | ActiveRecord `SELECT 1` against the primary connection, includes latency |
129
+ | `:cache` | `Rails.cache` read/write probe; works with Redis, Memcached, or in-process store |
130
+ | `:sidekiq` | Sidekiq Redis connectivity; optional `config.sidekiq_queue_size` threshold for queue depth |
131
+ | `:solid_queue` | Solid Queue DB connectivity; optional `config.solid_queue_job_count` threshold for pending jobs |
132
+ | `:good_job` | GoodJob queue latency; optional `config.good_job_latency` (seconds) threshold for oldest pending job |
133
+ | `:resque` | Resque Redis connectivity; optional `config.resque_queue_size` threshold for total queue depth |
134
+ | `:disk` | Free disk bytes via `df`; optional `config.disk_warn_threshold` / `config.disk_critical_threshold` (bytes) and `config.disk_path` (default: `/`) |
135
+ | `:memory` | Process RSS via `ps`; optional `config.memory_threshold` (bytes) reports `degraded` when exceeded |
136
+ | `:http` | HTTP GET to `config.http_url`; reports `critical` if response code differs from `config.http_expected_status` (default: `200`) or a network error occurs |
129
137
 
130
138
  [↑ Back to top](#table-of-contents)
131
139
 
@@ -5,7 +5,22 @@ require "timeout"
5
5
  module RailsHealthChecks
6
6
  class CheckRegistry
7
7
  BUILT_INS = {
8
- database: -> { Checks::DatabaseCheck.new }
8
+ database: -> { Checks::DatabaseCheck.new },
9
+ cache: -> { Checks::CacheCheck.new },
10
+ sidekiq: -> { Checks::SidekiqCheck.new(queue_size: RailsHealthChecks.configuration.sidekiq_queue_size) },
11
+ solid_queue: -> { Checks::SolidQueueCheck.new(job_count: RailsHealthChecks.configuration.solid_queue_job_count) },
12
+ good_job: -> { Checks::GoodJobCheck.new(latency: RailsHealthChecks.configuration.good_job_latency) },
13
+ resque: -> { Checks::ResqueCheck.new(queue_size: RailsHealthChecks.configuration.resque_queue_size) },
14
+ memory: -> { Checks::MemoryCheck.new(threshold: RailsHealthChecks.configuration.memory_threshold) },
15
+ http: -> { Checks::HttpCheck.new(
16
+ url: RailsHealthChecks.configuration.http_url,
17
+ expected_status: RailsHealthChecks.configuration.http_expected_status
18
+ ) },
19
+ disk: -> { Checks::DiskCheck.new(
20
+ warn_threshold: RailsHealthChecks.configuration.disk_warn_threshold,
21
+ critical_threshold: RailsHealthChecks.configuration.disk_critical_threshold,
22
+ path: RailsHealthChecks.configuration.disk_path
23
+ ) }
9
24
  }.freeze
10
25
 
11
26
  def self.build(check_names)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class CacheCheck < Check
6
+ PROBE_KEY = "rails_health_checks:cache_probe"
7
+ PROBE_VALUE = "ok"
8
+
9
+ def call
10
+ measure do
11
+ Rails.cache.write(PROBE_KEY, PROBE_VALUE, expires_in: 10)
12
+ result = Rails.cache.read(PROBE_KEY)
13
+ raise "unexpected value: #{result.inspect}" unless result == PROBE_VALUE
14
+ end
15
+ pass
16
+ rescue StandardError => e
17
+ fail_with(e.message)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module RailsHealthChecks
6
+ module Checks
7
+ class DiskCheck < Check
8
+ def initialize(warn_threshold: nil, critical_threshold: nil, path: "/")
9
+ @warn_threshold = warn_threshold
10
+ @critical_threshold = critical_threshold
11
+ @path = path
12
+ end
13
+
14
+ def call
15
+ free = nil
16
+ measure { free = free_bytes }
17
+
18
+ if @critical_threshold && free < @critical_threshold
19
+ return fail_with("free disk space #{free} bytes below critical threshold #{@critical_threshold} bytes")
20
+ end
21
+
22
+ if @warn_threshold && free < @warn_threshold
23
+ return warn_with("free disk space #{free} bytes below warn threshold #{@warn_threshold} bytes")
24
+ end
25
+
26
+ pass
27
+ rescue StandardError => e
28
+ fail_with(e.message)
29
+ end
30
+
31
+ private
32
+
33
+ def free_bytes
34
+ out, _, status = Open3.capture3("df", "-Pk", @path.to_s)
35
+ raise "df command failed with exit status #{status.exitstatus}" unless status.success?
36
+
37
+ parts = out.split("\n").last.split
38
+ parts[3].to_i * 1024
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class GoodJobCheck < Check
6
+ def initialize(latency: nil)
7
+ unless defined?(::GoodJob)
8
+ raise LoadError, "GoodJob is not installed. Add `gem 'good_job'` to your Gemfile to use the :good_job check."
9
+ end
10
+
11
+ @latency = latency
12
+ end
13
+
14
+ def call
15
+ measure do
16
+ oldest = ::GoodJob::Job
17
+ .where(finished_at: nil, performed_at: nil)
18
+ .minimum(:created_at)
19
+
20
+ if @latency && oldest
21
+ age = (Time.current - oldest).to_i
22
+ return warn_with("queue latency #{age}s exceeds threshold #{@latency}s") if age > @latency
23
+ end
24
+ end
25
+ pass
26
+ rescue StandardError => e
27
+ fail_with(e.message)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+
6
+ module RailsHealthChecks
7
+ module Checks
8
+ class HttpCheck < Check
9
+ def initialize(url:, expected_status: 200)
10
+ @url = url
11
+ @expected_status = expected_status
12
+ end
13
+
14
+ def call
15
+ measure do
16
+ response = Net::HTTP.get_response(URI.parse(@url))
17
+ code = response.code.to_i
18
+ return fail_with("HTTP GET #{@url} returned #{code}, expected #{@expected_status}") if code != @expected_status
19
+ end
20
+ pass
21
+ rescue StandardError => e
22
+ fail_with(e.message)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module RailsHealthChecks
6
+ module Checks
7
+ class MemoryCheck < Check
8
+ def initialize(threshold: nil)
9
+ @threshold = threshold
10
+ end
11
+
12
+ def call
13
+ rss = nil
14
+ measure { rss = rss_bytes }
15
+
16
+ if @threshold && rss > @threshold
17
+ return warn_with("process RSS #{rss} bytes exceeds threshold #{@threshold} bytes")
18
+ end
19
+
20
+ pass
21
+ rescue StandardError => e
22
+ fail_with(e.message)
23
+ end
24
+
25
+ private
26
+
27
+ def rss_bytes
28
+ out, _, status = Open3.capture3("ps", "-o", "rss=", "-p", Process.pid.to_s)
29
+ raise "ps command failed with exit status #{status.exitstatus}" unless status.success?
30
+
31
+ out.strip.to_i * 1024
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class ResqueCheck < Check
6
+ def initialize(queue_size: nil)
7
+ unless defined?(::Resque)
8
+ raise LoadError, "Resque is not installed. Add `gem 'resque'` to your Gemfile to use the :resque check."
9
+ end
10
+
11
+ @queue_size = queue_size
12
+ end
13
+
14
+ def call
15
+ measure { ::Resque.redis.ping }
16
+
17
+ if @queue_size
18
+ depth = ::Resque.queues.sum { |q| ::Resque.size(q) }
19
+ return warn_with("queue depth #{depth} exceeds threshold #{@queue_size}") if depth > @queue_size
20
+ end
21
+
22
+ pass
23
+ rescue StandardError => e
24
+ fail_with(e.message)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class SidekiqCheck < Check
6
+ def initialize(queue_size: nil)
7
+ unless defined?(::Sidekiq)
8
+ raise LoadError, "Sidekiq is not installed. Add `gem 'sidekiq'` to your Gemfile to use the :sidekiq check."
9
+ end
10
+
11
+ @queue_size = queue_size
12
+ end
13
+
14
+ def call
15
+ measure { ::Sidekiq.redis { |conn| conn.ping } }
16
+
17
+ if @queue_size
18
+ depth = ::Sidekiq::Queue.all.sum(&:size)
19
+ return warn_with("queue depth #{depth} exceeds threshold #{@queue_size}") if depth > @queue_size
20
+ end
21
+
22
+ pass
23
+ rescue StandardError => e
24
+ fail_with(e.message)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsHealthChecks
4
+ module Checks
5
+ class SolidQueueCheck < Check
6
+ def initialize(job_count: nil)
7
+ unless defined?(::SolidQueue)
8
+ raise LoadError, "SolidQueue is not installed. Add `gem 'solid_queue'` to your Gemfile to use the :solid_queue check."
9
+ end
10
+
11
+ @job_count = job_count
12
+ end
13
+
14
+ def call
15
+ measure do
16
+ pending = ::SolidQueue::ReadyExecution.count
17
+ if @job_count && pending > @job_count
18
+ return warn_with("pending job count #{pending} exceeds threshold #{@job_count}")
19
+ end
20
+ end
21
+ pass
22
+ rescue StandardError => e
23
+ fail_with(e.message)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module RailsHealthChecks
4
4
  class Configuration
5
- attr_accessor :checks, :timeout, :allowed_ips, :token
5
+ attr_accessor :checks, :timeout, :allowed_ips, :token, :sidekiq_queue_size, :solid_queue_job_count, :good_job_latency,
6
+ :resque_queue_size, :disk_warn_threshold, :disk_critical_threshold, :disk_path,
7
+ :memory_threshold, :http_url, :http_expected_status
6
8
  attr_reader :authenticate_block
7
9
 
8
10
  def initialize
@@ -11,6 +13,16 @@ module RailsHealthChecks
11
13
  @allowed_ips = nil
12
14
  @token = nil
13
15
  @authenticate_block = nil
16
+ @sidekiq_queue_size = nil
17
+ @solid_queue_job_count = nil
18
+ @good_job_latency = nil
19
+ @resque_queue_size = nil
20
+ @disk_warn_threshold = nil
21
+ @disk_critical_threshold = nil
22
+ @disk_path = "/"
23
+ @memory_threshold = nil
24
+ @http_url = nil
25
+ @http_expected_status = 200
14
26
  end
15
27
 
16
28
  def authenticate(&block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsHealthChecks
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -6,6 +6,14 @@ require "rails_health_checks/configuration"
6
6
  require "rails_health_checks/authentication"
7
7
  require "rails_health_checks/check"
8
8
  require "rails_health_checks/checks/database_check"
9
+ require "rails_health_checks/checks/cache_check"
10
+ require "rails_health_checks/checks/sidekiq_check"
11
+ require "rails_health_checks/checks/solid_queue_check"
12
+ require "rails_health_checks/checks/good_job_check"
13
+ require "rails_health_checks/checks/resque_check"
14
+ require "rails_health_checks/checks/disk_check"
15
+ require "rails_health_checks/checks/memory_check"
16
+ require "rails_health_checks/checks/http_check"
9
17
  require "rails_health_checks/check_registry"
10
18
  require "rails_health_checks/response_builder"
11
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_health_checks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -44,7 +44,15 @@ files:
44
44
  - lib/rails_health_checks/authentication.rb
45
45
  - lib/rails_health_checks/check.rb
46
46
  - lib/rails_health_checks/check_registry.rb
47
+ - lib/rails_health_checks/checks/cache_check.rb
47
48
  - lib/rails_health_checks/checks/database_check.rb
49
+ - lib/rails_health_checks/checks/disk_check.rb
50
+ - lib/rails_health_checks/checks/good_job_check.rb
51
+ - lib/rails_health_checks/checks/http_check.rb
52
+ - lib/rails_health_checks/checks/memory_check.rb
53
+ - lib/rails_health_checks/checks/resque_check.rb
54
+ - lib/rails_health_checks/checks/sidekiq_check.rb
55
+ - lib/rails_health_checks/checks/solid_queue_check.rb
48
56
  - lib/rails_health_checks/configuration.rb
49
57
  - lib/rails_health_checks/engine.rb
50
58
  - lib/rails_health_checks/response_builder.rb