94 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module Gitlab
 | 
						|
  # Reference Counter
 | 
						|
  #
 | 
						|
  # A reference counter is used as a mechanism to identify when
 | 
						|
  # a repository is being accessed by a writable operation.
 | 
						|
  #
 | 
						|
  # Maintenance operations would use this as a clue to when it should
 | 
						|
  # execute significant changes in order to avoid disrupting running traffic
 | 
						|
  class ReferenceCounter
 | 
						|
    REFERENCE_EXPIRE_TIME = 600
 | 
						|
 | 
						|
    attr_reader :gl_repository, :key
 | 
						|
 | 
						|
    # Reference Counter instance
 | 
						|
    #
 | 
						|
    # @example
 | 
						|
    #   Gitlab::ReferenceCounter.new('project-1')
 | 
						|
    #
 | 
						|
    # @see Gitlab::GlRepository::RepoType.identifier_for_repositorable
 | 
						|
    # @param [String] gl_repository repository identifier
 | 
						|
    def initialize(gl_repository)
 | 
						|
      @gl_repository = gl_repository
 | 
						|
      @key = "git-receive-pack-reference-counter:#{gl_repository}"
 | 
						|
    end
 | 
						|
 | 
						|
    # Return the actual counter value
 | 
						|
    #
 | 
						|
    # @return [Integer] value
 | 
						|
    def value
 | 
						|
      Gitlab::Redis::SharedState.with do |redis|
 | 
						|
        (redis.get(key) || 0).to_i
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Increase the counter
 | 
						|
    #
 | 
						|
    # @return [Boolean] whether operation was a success
 | 
						|
    def increase
 | 
						|
      redis_cmd do |redis|
 | 
						|
        redis.incr(key)
 | 
						|
        redis.expire(key, REFERENCE_EXPIRE_TIME)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Decrease the counter
 | 
						|
    #
 | 
						|
    # @return [Boolean] whether operation was a success
 | 
						|
    def decrease
 | 
						|
      redis_cmd do |redis|
 | 
						|
        current_value = redis.decr(key)
 | 
						|
        if current_value < 0
 | 
						|
          Gitlab::AppLogger.warn("Reference counter for #{gl_repository} decreased" \
 | 
						|
            " when its value was less than 1. Resetting the counter.")
 | 
						|
          redis.del(key)
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Reset the reference counter
 | 
						|
    #
 | 
						|
    # @private Used internally by SRE and debugging purpose
 | 
						|
    # @return [Boolean] whether reset was a success
 | 
						|
    def reset!
 | 
						|
      redis_cmd do |redis|
 | 
						|
        redis.del(key)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # When the reference counter would expire
 | 
						|
    #
 | 
						|
    # @api private Used internally by SRE and debugging purpose
 | 
						|
    # @return [Integer] Number in seconds until expiration or false if never
 | 
						|
    def expires_in
 | 
						|
      Gitlab::Redis::SharedState.with do |redis|
 | 
						|
        redis.ttl(key)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    def redis_cmd
 | 
						|
      Gitlab::Redis::SharedState.with { |redis| yield(redis) }
 | 
						|
 | 
						|
      true
 | 
						|
    rescue StandardError => e
 | 
						|
      Gitlab::AppLogger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}")
 | 
						|
 | 
						|
      false
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |