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

update_repository_storage_methods.rb « concerns « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f14c79ecd7ef92ca27c0536dee72ed53d705c15a (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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true

module UpdateRepositoryStorageMethods
  include Gitlab::Utils::StrongMemoize

  Error = Class.new(StandardError)

  attr_reader :repository_storage_move

  delegate :container, :source_storage_name, :destination_storage_name, to: :repository_storage_move

  def initialize(repository_storage_move)
    @repository_storage_move = repository_storage_move
  end

  def execute
    response = repository_storage_move.with_lock do
      next ServiceResponse.success unless repository_storage_move.scheduled?

      repository_storage_move.start!

      nil
    end

    return response if response

    unless same_filesystem?
      mirror_repositories

      repository_storage_move.transaction do
        mirror_object_pool(destination_storage_name)
      end
    end

    repository_storage_move.transaction do
      repository_storage_move.finish_replication!

      track_repository(destination_storage_name)
    end

    remove_old_paths unless same_filesystem?

    repository_storage_move.finish_cleanup!

    ServiceResponse.success
  rescue StandardError => e
    repository_storage_move.do_fail!

    Gitlab::ErrorTracking.track_and_raise_exception(e, container_klass: container.class.to_s, container_path: container.full_path)
  end

  private

  def track_repository(destination_shard)
    raise NotImplementedError
  end

  def mirror_repositories
    raise NotImplementedError
  end

  def mirror_object_pool(_destination_shard)
    # no-op, redefined for Projects::UpdateRepositoryStorageService
    nil
  end

  def mirror_repository(type:)
    unless wait_for_pushes(type)
      raise Error, s_('UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes') % { type: type.name }
    end

    # `Projects::UpdateRepositoryStorageService`` expects the repository it is
    # moving to have a `Project` as a container.
    # This hack allows design repos to also be moved as part of a project move
    # as before.
    # The alternative to this hack is to setup a service like
    # `Snippets::UpdateRepositoryStorageService' and a corresponding worker like
    # `Snippets::UpdateRepositoryStorageWorker` for snippets.
    #
    # Gitlab issue: https://gitlab.com/gitlab-org/gitlab/-/issues/423429

    repository = type.repository_for(type.design? ? container.design_management_repository : container)
    full_path = repository.full_path
    raw_repository = repository.raw
    checksum = repository.checksum

    # Initialize a git repository on the target path
    new_repository = Gitlab::Git::Repository.new(
      destination_storage_name,
      raw_repository.relative_path,
      raw_repository.gl_repository,
      full_path
    )

    new_repository.replicate(raw_repository)
    new_checksum = new_repository.checksum

    if checksum != new_checksum
      raise Error, s_('UpdateRepositoryStorage|Failed to verify %{type} repository checksum from %{old} to %{new}') % { type: type.name, old: checksum, new: new_checksum }
    end
  end

  def same_filesystem?
    strong_memoize(:same_filesystem) do
      Gitlab::GitalyClient.filesystem_id(source_storage_name) == Gitlab::GitalyClient.filesystem_id(destination_storage_name)
    end
  end

  def remove_old_paths
    if container.repository_exists?
      Gitlab::Git::Repository.new(
        source_storage_name,
        "#{container.disk_path}.git",
        nil,
        nil
      ).remove
    end
  end

  def wait_for_pushes(type)
    reference_counter = container.reference_counter(type: type)

    # Try for 30 seconds, polling every 10
    3.times do
      return true if reference_counter.value == 0

      sleep 10
    end

    false
  end
end