summaryrefslogtreecommitdiff
path: root/lib/gitlab/optimistic_locking.rb
blob: 9f39b5f122fffe7cda15a1f4bf5db298cb8944db (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
# frozen_string_literal: true

module Gitlab
  module OptimisticLocking
    MAX_RETRIES = 100

    module_function

    def retry_lock(subject, max_retries = MAX_RETRIES, name:, &block)
      start_time = Gitlab::Metrics::System.monotonic_time
      retry_attempts = 0

      begin
        subject.transaction do
          yield(subject)
        end
      rescue ActiveRecord::StaleObjectError
        raise unless retry_attempts < max_retries

        subject.reset

        retry_attempts += 1
        retry
      ensure
        retry_lock_histogram.observe({}, retry_attempts)

        log_optimistic_lock_retries(
          name: name,
          retry_attempts: retry_attempts,
          start_time: start_time)
      end
    end

    alias_method :retry_optimistic_lock, :retry_lock

    def log_optimistic_lock_retries(name:, retry_attempts:, start_time:)
      return unless retry_attempts > 0

      elapsed_time = Gitlab::Metrics::System.monotonic_time - start_time

      retry_lock_logger.info(
        message: "Optimistic Lock released with retries",
        name: name,
        retries: retry_attempts,
        time_s: elapsed_time)
    end

    def retry_lock_logger
      @retry_lock_logger ||= Gitlab::Services::Logger.build
    end

    def retry_lock_histogram
      @retry_lock_histogram ||=
        Gitlab::Metrics.histogram(
          :gitlab_optimistic_locking_retries,
          'Number of retry attempts to execute optimistic retry lock',
          {},
          [0, 1, 2, 3, 5, 10, 50]
        )
    end
  end
end