Module: ExclusiveLeaseGuard

Overview

Concern that helps with getting an exclusive lease for running a block of code.

‘#try_obtain_lease` takes a block which will be run if it was able to obtain the lease. Implement `#lease_timeout` to configure the timeout for the exclusive lease.

Optionally override ‘#lease_key` to set the lease key, it defaults to the class name with underscores.

Optionally override ‘#lease_release?` to prevent the job to be re-executed more often than LEASE_TIMEOUT.

Instance Method Summary collapse

Instance Method Details

#exclusive_leaseObject



39
40
41
# File 'app/services/concerns/exclusive_lease_guard.rb', line 39

def exclusive_lease
  @lease ||= Gitlab::ExclusiveLease.new(lease_key, timeout: lease_timeout)
end

#lease_keyObject



43
44
45
# File 'app/services/concerns/exclusive_lease_guard.rb', line 43

def lease_key
  @lease_key ||= self.class.name.underscore
end

#lease_release?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'app/services/concerns/exclusive_lease_guard.rb', line 52

def lease_release?
  true
end

#lease_taken_log_levelObject



80
81
82
# File 'app/services/concerns/exclusive_lease_guard.rb', line 80

def lease_taken_log_level
  :error
end

#lease_taken_messageObject



76
77
78
# File 'app/services/concerns/exclusive_lease_guard.rb', line 76

def lease_taken_message
  "Cannot obtain an exclusive lease. There must be another instance already in execution."
end

#lease_timeoutObject

Raises:

  • (NotImplementedError)


47
48
49
50
# File 'app/services/concerns/exclusive_lease_guard.rb', line 47

def lease_timeout
  raise NotImplementedError,
    "#{self.class.name} does not implement #{__method__}"
end

#log_lease_takenObject



64
65
66
67
68
69
70
71
72
73
74
# File 'app/services/concerns/exclusive_lease_guard.rb', line 64

def log_lease_taken
  logger = Gitlab::AppJsonLogger
  args = { message: lease_taken_message, lease_key: lease_key, class_name: self.class.name, lease_timeout: lease_timeout }

  case lease_taken_log_level
  when :debug then logger.debug(args)
  when :info  then logger.info(args)
  when :warn  then logger.warn(args)
  else             logger.error(args)
  end
end

#release_lease(uuid) ⇒ Object



56
57
58
# File 'app/services/concerns/exclusive_lease_guard.rb', line 56

def release_lease(uuid)
  Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end

#renew_lease!Object



60
61
62
# File 'app/services/concerns/exclusive_lease_guard.rb', line 60

def renew_lease!
  exclusive_lease.renew
end

#try_obtain_leaseObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'app/services/concerns/exclusive_lease_guard.rb', line 20

def try_obtain_lease
  lease = exclusive_lease.try_obtain

  Gitlab::Instrumentation::ExclusiveLock.increment_requested_count

  unless lease
    log_lease_taken
    return
  end

  begin
    lease_start_time = Time.current
    yield lease
  ensure
    Gitlab::Instrumentation::ExclusiveLock.add_hold_duration(Time.current - lease_start_time)
    release_lease(lease) if lease_release?
  end
end