Skip to content

Commit

Permalink
Expose Pitchfork::Info.workers_count and .live_workers_count
Browse files Browse the repository at this point in the history
This allow health check endpoints to consider the state of the
whole Pitchfork instance. e.g. you may want to fail the health
check if more than X% of the workers aren't live.
  • Loading branch information
byroot committed Jul 10, 2023
1 parent 6339414 commit b01606e
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Expose `Pitchfork::Info.workers_count` and `.live_workers_count` to be consumed by application health checks.
- Implement `before_worker_exit` callback.
- Make each mold and worker a process group leader.
- Get rid of `Pitchfork::PrereadInput`.
Expand Down
3 changes: 3 additions & 0 deletions lib/pitchfork/http_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'pitchfork/flock'
require 'pitchfork/soft_timeout'
require 'pitchfork/shared_memory'
require 'pitchfork/info'

module Pitchfork
# This is the process manager of Pitchfork. This manages worker
Expand Down Expand Up @@ -165,6 +166,8 @@ def initialize(app, options = {})
# list of signals we care about and trap in master.
@queue_sigs = [
:QUIT, :INT, :TERM, :USR2, :TTIN, :TTOU ]

Info.workers_count = worker_processes
SharedMemory.preallocate_drops(worker_processes)
end

Expand Down
18 changes: 18 additions & 0 deletions lib/pitchfork/info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require 'pitchfork/shared_memory'

module Pitchfork
module Info
class << self
attr_accessor :workers_count

def live_workers_count
now = Pitchfork.time_now(true)
(0...workers_count).count do |nr|
SharedMemory.worker_deadline(nr).value > now
end
end
end
end
end
10 changes: 10 additions & 0 deletions test/integration/info.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use Rack::ContentLength
use Rack::ContentType, "text/plain"
run lambda { |env|
info = {
workers_count: Pitchfork::Info.workers_count,
live_workers_count: Pitchfork::Info.live_workers_count,
}

[ 200, {}, [ info.inspect ] ]
}
19 changes: 19 additions & 0 deletions test/integration/test_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'integration_test_helper'

class InfoTest < Pitchfork::IntegrationTest
def test_after_request_complete
addr, port = unused_port

pid = spawn_server(app: File.join(ROOT, "test/integration/info.ru"), config: <<~CONFIG)
listen "#{addr}:#{port}"
worker_processes 2
CONFIG

assert_healthy("http://#{addr}:#{port}")

response = http_get("http://#{addr}:#{port}/")
assert_equal "{:workers_count=>2, :live_workers_count=>2}", response.body

assert_clean_shutdown(pid)
end
end
17 changes: 13 additions & 4 deletions test/integration_test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,24 @@ def wait_healthy?(host, timeout)
end

def healthy?(url)
uri = URI(url)
http = Net::HTTP.start(uri.host, uri.port)
http.max_retries = 0
http.get("#{uri.path}?#{uri.query}")
http_get(url)
true
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL, EOFError
false
end

def http_get(url)
uri = URI(url)
http = Net::HTTP.start(uri.host, uri.port)
http.max_retries = 0

path_info = uri.path.empty? ? "/" : uri.path
if uri.query
path_info = "#{path_info}?#{uri.query}"
end
http.get(path_info)
end

def spawn_server(*args, app:, config:, lint: true)
File.write("pitchfork.conf.rb", config)
env = lint ? { "RACK_ENV" => "development" } : {}
Expand Down

0 comments on commit b01606e

Please sign in to comment.