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

forked_storage_check.rb « storage « git « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 557a3b0e61dae39ebf8249519db855923118b0e0 (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
module Gitlab
  module Git
    module Storage
      module ForkedStorageCheck
        extend self

        def storage_available?(path, timeout_seconds = 5)
          status = timeout_check(path, timeout_seconds)

          status.success?
        end

        def timeout_check(path, timeout_seconds)
          filesystem_check_pid = check_filesystem_in_fork(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 call forks out into a process, that process will then be replaced
        # With an `exec` call, since we fork out into a shell, we can create a
        # child process without needing an ActiveRecord-connection.
        #
        # Inside the shell, we use `& wait` to fork another child. We do this
        # to prevent leaving a zombie process when the parent gets killed by the
        # timeout.
        #
        # https://stackoverflow.com/questions/27892975/what-causes-activerecord-breaking-postgres-connection-after-forking
        # https://stackoverflow.com/questions/22012943/activerecordstatementinvalid-runtimeerror-the-connection-cannot-be-reused-in
        def check_filesystem_in_fork(path)
          fork do
            STDOUT.reopen('/dev/null')
            STDERR.reopen('/dev/null')

            exec("(#{test_script(path)}) & wait %1")
          end
        end

        def test_script(path)
          "testpath=\"$(realpath #{Shellwords.escape(path)})\" && stat $testpath"
        end
      end
    end
  end
end