gitlab-org--gitlab-foss/lib/gitlab/sidekiq_signals.rb

46 lines
1.6 KiB
Ruby

# frozen_string_literal: true
module Gitlab
# As a process group leader, we can ensure that children of sidekiq are killed
# at the same time as sidekiq itself, to stop long-lived children from being
# reparented to init and "escaping". To do this, we override the default
# handlers used by sidekiq for INT and TERM signals
module SidekiqSignals
REPLACE_SIGNALS = %w[INT TERM].freeze
SIDEKIQ_CHANGED_MESSAGE =
"Intercepting signal handlers: #{REPLACE_SIGNALS.join(", ")} failed. " \
"Sidekiq should have registered them, but appears not to have done so."
def self.install!(sidekiq_handlers)
# This only works if we're process group leader
return unless Process.getpgrp == Process.pid
raise SIDEKIQ_CHANGED_MESSAGE unless
REPLACE_SIGNALS == sidekiq_handlers.keys & REPLACE_SIGNALS
REPLACE_SIGNALS.each do |signal|
old_handler = sidekiq_handlers[signal]
sidekiq_handlers[signal] = ->(cli) do
blindly_signal_pgroup!(signal)
old_handler.call(cli)
end
end
end
# The process group leader can forward INT and TERM signals to the whole
# group. However, the forwarded signal is *also* received by the leader,
# which could lead to an infinite loop. We can avoid this by temporarily
# ignoring the forwarded signal. This may cause us to miss some repeated
# signals from outside the process group, but that isn't fatal.
def self.blindly_signal_pgroup!(signal)
old_trap = trap(signal, 'IGNORE')
begin
Process.kill(signal, 0)
ensure
trap(signal, old_trap)
end
end
end
end