65 lines
2.1 KiB
Ruby
65 lines
2.1 KiB
Ruby
module Gitlab
|
|
module Git
|
|
module Storage
|
|
module ForkedStorageCheck
|
|
extend self
|
|
|
|
def storage_available?(path, timeout_seconds = 5, retries = 1)
|
|
partial_timeout = timeout_seconds / retries
|
|
status = timeout_check(path, partial_timeout)
|
|
|
|
# If the status check did not succeed the first time, we retry a few
|
|
# more times to avoid one-off failures
|
|
current_attempts = 1
|
|
while current_attempts < retries && !status.success?
|
|
status = timeout_check(path, partial_timeout)
|
|
current_attempts += 1
|
|
end
|
|
|
|
status.success?
|
|
end
|
|
|
|
def timeout_check(path, timeout_seconds)
|
|
filesystem_check_pid = check_filesystem_in_process(path)
|
|
|
|
deadline = timeout_seconds.seconds.from_now.utc
|
|
wait_time = 0.01
|
|
status = nil
|
|
|
|
while status.nil?
|
|
|
|
if deadline > Time.now.utc
|
|
sleep(wait_time)
|
|
_pid, status = Process.wait2(filesystem_check_pid, Process::WNOHANG)
|
|
else
|
|
Process.kill('KILL', filesystem_check_pid)
|
|
# Blocking wait, so we are sure the process is gone before continuing
|
|
_pid, status = Process.wait2(filesystem_check_pid)
|
|
end
|
|
end
|
|
|
|
status
|
|
end
|
|
|
|
# This will spawn a new 2 processes to do the check:
|
|
# The outer child (waiter) will spawn another child process (stater).
|
|
#
|
|
# The stater is the process is performing the actual filesystem check
|
|
# the check might hang if the filesystem is acting up.
|
|
# In this case we will send a `KILL` to the waiter, which will still
|
|
# be responsive while the stater is hanging.
|
|
def check_filesystem_in_process(path)
|
|
spawn('ruby', '-e', ruby_check, path, [:out, :err] => '/dev/null')
|
|
end
|
|
|
|
def ruby_check
|
|
<<~RUBY_FILESYSTEM_CHECK
|
|
inner_pid = fork { File.stat(ARGV.first) }
|
|
Process.waitpid(inner_pid)
|
|
exit $?.exitstatus
|
|
RUBY_FILESYSTEM_CHECK
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|