497acb1670
PumaWorkerKiller is used for periodically checking and killing workers (the biggest one) if overall memory reaches specified limit. This metric allows us to watch number of killed workers.
82 lines
2.4 KiB
Ruby
82 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
require 'fileutils'
|
|
require 'excon'
|
|
|
|
describe 'Puma' do
|
|
before(:all) do
|
|
project_root = Rails.root.to_s
|
|
config_lines = File.read(Rails.root.join('config/puma.example.development.rb'))
|
|
.gsub('config.ru', File.join(__dir__, 'configs/config.ru'))
|
|
.gsub('workers 2', 'workers 1')
|
|
.gsub('/home/git/gitlab.socket', File.join(project_root, 'tmp/tests/puma.socket'))
|
|
.gsub('on_worker_boot do', "on_worker_boot do\nFile.write('#{File.join(project_root, 'tmp/tests/puma-worker-ready')}', Process.pid)")
|
|
.gsub(%r{/home/git(/gitlab)?}, project_root)
|
|
config_path = File.join(project_root, 'tmp/tests/puma.rb')
|
|
@socket_path = File.join(project_root, 'tmp/tests/puma.socket')
|
|
|
|
File.write(config_path, config_lines)
|
|
|
|
cmd = %W[puma -e test -C #{config_path} #{File.join(__dir__, 'configs/config.ru')}]
|
|
@puma_master_pid = spawn({ 'DISABLE_PUMA_WORKER_KILLER' => '1' }, *cmd)
|
|
wait_puma_boot!(@puma_master_pid, File.join(project_root, 'tmp/tests/puma-worker-ready'))
|
|
WebMock.allow_net_connect!
|
|
end
|
|
|
|
%w[SIGQUIT SIGTERM SIGKILL].each do |signal|
|
|
it "has a worker that self-terminates on signal #{signal}" do
|
|
response = Excon.get('unix://', socket: @socket_path)
|
|
expect(response.status).to eq(200)
|
|
|
|
worker_pid = response.body.to_i
|
|
expect(worker_pid).to be > 0
|
|
|
|
begin
|
|
Excon.post("unix://?#{signal}", socket: @socket_path)
|
|
rescue Excon::Error::Socket
|
|
# The connection may be closed abruptly
|
|
end
|
|
|
|
expect(pid_gone?(worker_pid)).to eq(true)
|
|
end
|
|
end
|
|
|
|
after(:all) do
|
|
WebMock.disable_net_connect!(allow_localhost: true)
|
|
Process.kill('TERM', @puma_master_pid)
|
|
rescue Errno::ESRCH
|
|
end
|
|
|
|
def wait_puma_boot!(master_pid, ready_file)
|
|
# We have seen the boot timeout after 2 minutes in CI so let's set it to 5 minutes.
|
|
timeout = 5 * 60
|
|
timeout.times do
|
|
return if File.exist?(ready_file)
|
|
|
|
pid = Process.waitpid(master_pid, Process::WNOHANG)
|
|
raise "puma failed to boot: #{$?}" unless pid.nil?
|
|
|
|
sleep 1
|
|
end
|
|
|
|
raise "puma boot timed out after #{timeout} seconds"
|
|
end
|
|
|
|
def pid_gone?(pid)
|
|
# Worker termination should take less than a second. That makes 10
|
|
# seconds a generous timeout.
|
|
10.times do
|
|
begin
|
|
Process.kill(0, pid)
|
|
rescue Errno::ESRCH
|
|
return true
|
|
end
|
|
|
|
sleep 1
|
|
end
|
|
|
|
false
|
|
end
|
|
end
|