diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 6bd90e71bf3..f6db71be438 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -2,14 +2,13 @@ module Ci class Runner < ActiveRecord::Base extend Gitlab::Ci::Model include Gitlab::SQL::Pattern - include Gitlab::Utils::StrongMemoize + include RedisCacheable RUNNER_QUEUE_EXPIRY_TIME = 60.minutes ONLINE_CONTACT_TIMEOUT = 1.hour UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes AVAILABLE_SCOPES = %w[specific shared active paused online].freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level].freeze - CACHED_ATTRIBUTES_EXPIRY_TIME = 24.hours has_many :builds has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -50,6 +49,8 @@ module Ci ref_protected: 1 } + cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at + # Searches for runners matching the given query. # # This method uses ILIKE on PostgreSQL and LIKE on MySQL. @@ -70,16 +71,6 @@ module Ci ONLINE_CONTACT_TIMEOUT.ago end - def self.cached_attr_reader(*attributes) - attributes.each do |attribute| - define_method("#{attribute}") do - cached_attribute(attribute) || read_attribute(attribute) - end - end - end - - cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at - def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? end @@ -189,10 +180,6 @@ module Ci "runner:build_queue:#{self.token}" end - def cache_attribute_key - "runner:info:#{self.id}" - end - def persist_cached_data? # Use a random threshold to prevent beating DB updates. # It generates a distribution between [40m, 80m]. @@ -204,25 +191,6 @@ module Ci (Time.now - real_contacted_at) >= contacted_at_max_age end - def cached_attribute(key) - (cached_attributes || {})[key] - end - - def cached_attributes - strong_memoize(:cached_attributes) do - Gitlab::Redis::SharedState.with do |redis| - data = redis.get(cache_attribute_key) - JSON.parse(data, symbolize_names: true) if data - end - end - end - - def cache_attributes(values) - Gitlab::Redis::SharedState.with do |redis| - redis.set(cache_attribute_key, values.to_json, ex: CACHED_ATTRIBUTES_EXPIRY_TIME) - end - end - def tag_constraints unless has_tags? || run_untagged? errors.add(:tags_list, diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb new file mode 100644 index 00000000000..249ebf9af07 --- /dev/null +++ b/app/models/concerns/redis_cacheable.rb @@ -0,0 +1,41 @@ +module RedisCacheable + extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize + + CACHED_ATTRIBUTES_EXPIRY_TIME = 24.hours + + class_methods do + def cached_attr_reader(*attributes) + attributes.each do |attribute| + define_method("#{attribute}") do + cached_attribute(attribute) || read_attribute(attribute) + end + end + end + end + + def cached_attribute(attribute) + (cached_attributes || {})[attribute] + end + + def cache_attributes(values) + Gitlab::Redis::SharedState.with do |redis| + redis.set(cache_attribute_key, values.to_json, ex: CACHED_ATTRIBUTES_EXPIRY_TIME) + end + end + + private + + def cache_attribute_key + "#{self.class.name}:attributes:#{self.id}" + end + + def cached_attributes + strong_memoize(:cached_attributes) do + Gitlab::Redis::SharedState.with do |redis| + data = redis.get(cache_attribute_key) + JSON.parse(data, symbolize_names: true) if data + end + end + end +end diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb new file mode 100644 index 00000000000..3edb69f4666 --- /dev/null +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe RedisCacheable do + let(:runner) { build(:ci_runner) } + + describe '#cached_attribute' do + subject { runner.cached_attribute(:anything) } + + it 'gets the cache attribute' do + Gitlab::Redis::SharedState.with do |redis| + expect(redis).to receive(:get).with(runner.send(:cache_attribute_key)) + end + + subject + end + end + + describe '#cache_attributes' do + let(:values) { { name: 'new_name' } } + + subject { runner.cache_attributes(values) } + + it 'sets the cache attributes' do + Gitlab::Redis::SharedState.with do |redis| + values.each do |key, value| + redis_key = runner.send(:cache_attribute_key) + expect(redis).to receive(:set).with(redis_key, values.to_json, anything) + end + end + + subject + end + end +end