Welcome to mirror list, hosted at ThFree Co, Russian Federation.

forked_storage_check.rb « storage « git « gitlab « lib « gitlab_git « vendor « ruby - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0a4e557b59bd78b6ae5fb3bd5b32b1e7e9cc274e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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