Commit c92e1d73 authored by Matija Čupić's avatar Matija Čupić

Improve runner attribute cachine

parent 3366f377
...@@ -2,12 +2,14 @@ module Ci ...@@ -2,12 +2,14 @@ module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Gitlab::Ci::Model extend Gitlab::Ci::Model
include Gitlab::SQL::Pattern include Gitlab::SQL::Pattern
include Gitlab::Utils::StrongMemoize
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour ONLINE_CONTACT_TIMEOUT = 1.hour
UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level].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 :builds
has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
...@@ -68,29 +70,15 @@ module Ci ...@@ -68,29 +70,15 @@ module Ci
ONLINE_CONTACT_TIMEOUT.ago ONLINE_CONTACT_TIMEOUT.ago
end end
def name def self.cached_attr_reader(*attributes)
cached_attribute(:name) || read_attribute(:name) attributes.each do |attribute|
define_method("#{attribute}") do
cached_attribute(attribute) || read_attribute(attribute)
end end
def version
cached_attribute(:version) || read_attribute(:version)
end
def revision
cached_attribute(:revision) || read_attribute(:revision)
end end
def platform
cached_attribute(:platform) || read_attribute(:platform)
end
def architecture
cached_attribute(:architecture) || read_attribute(:architecture)
end end
def contacted_at cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at
cached_attribute(:contacted_at) || read_attribute(:contacted_at)
end
def set_default_values def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank? self.token = SecureRandom.hex(15) if self.token.blank?
...@@ -178,7 +166,7 @@ module Ci ...@@ -178,7 +166,7 @@ module Ci
end end
def update_cached_info(values) def update_cached_info(values)
values = values&.slice(:name, :version, :revision, :platform, :architecture) || {} values = values&.slice(:version, :revision, :platform, :architecture) || {}
values[:contacted_at] = Time.now values[:contacted_at] = Time.now
cache_attributes(values) cache_attributes(values)
...@@ -201,8 +189,8 @@ module Ci ...@@ -201,8 +189,8 @@ module Ci
"runner:build_queue:#{self.token}" "runner:build_queue:#{self.token}"
end end
def cache_attribute_key(key) def cache_attribute_key
"runner:info:#{self.id}:#{key}" "runner:info:#{self.id}"
end end
def persist_cached_data? def persist_cached_data?
...@@ -217,17 +205,21 @@ module Ci ...@@ -217,17 +205,21 @@ module Ci
end end
def cached_attribute(key) def cached_attribute(key)
@cached_attributes = {} (cached_attributes || {})[key]
@cached_attributes[key] ||= Gitlab::Redis::SharedState.with do |redis| end
redis.get(cache_attribute_key(key))
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 end
def cache_attributes(values) def cache_attributes(values)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::SharedState.with do |redis|
values.each do |key, value| redis.set(cache_attribute_key, values.to_json, ex: CACHED_ATTRIBUTES_EXPIRY_TIME)
redis.set(cache_attribute_key(key), value, ex: 24.hours)
end
end end
end end
......
...@@ -95,6 +95,12 @@ describe Ci::Runner do ...@@ -95,6 +95,12 @@ describe Ci::Runner do
subject { runner.online? } subject { runner.online? }
before do
allow_any_instance_of(described_class).to receive(:cached_attribute).and_call_original
allow_any_instance_of(described_class).to receive(:cached_attribute)
.with(:platform).and_return("darwin")
end
context 'no cache value' do context 'no cache value' do
before do before do
stub_redis_runner_contacted_at(nil) stub_redis_runner_contacted_at(nil)
...@@ -147,8 +153,9 @@ describe Ci::Runner do ...@@ -147,8 +153,9 @@ describe Ci::Runner do
def stub_redis_runner_contacted_at(value) def stub_redis_runner_contacted_at(value)
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::SharedState.with do |redis|
cache_key = runner.send(:cache_attribute_key, :contacted_at) cache_key = runner.send(:cache_attribute_key)
expect(redis).to receive(:get).with(cache_key).and_return(value).at_least(:once) expect(redis).to receive(:get).with(cache_key)
.and_return({ contacted_at: value }.to_json).at_least(:once)
end end
end end
end end
...@@ -405,7 +412,7 @@ describe Ci::Runner do ...@@ -405,7 +412,7 @@ describe Ci::Runner do
end end
it 'updates cache' do it 'updates cache' do
expect_redis_update(:architecture, :contacted_at) expect_redis_update
subject subject
end end
...@@ -417,28 +424,26 @@ describe Ci::Runner do ...@@ -417,28 +424,26 @@ describe Ci::Runner do
end end
it 'updates database' do it 'updates database' do
expect_redis_update(:architecture, :contacted_at) expect_redis_update
expect { subject }.to change { runner.reload.read_attribute(:contacted_at) } expect { subject }.to change { runner.reload.read_attribute(:contacted_at) }
.and change { runner.reload.read_attribute(:architecture) } .and change { runner.reload.read_attribute(:architecture) }
end end
it 'updates cache' do it 'updates cache' do
expect_redis_update(:architecture, :contacted_at) expect_redis_update
subject subject
end end
end end
def expect_redis_update(*params) def expect_redis_update
Gitlab::Redis::SharedState.with do |redis| Gitlab::Redis::SharedState.with do |redis|
params.each do |param| redis_key = runner.send(:cache_attribute_key)
redis_key = runner.send(:cache_attribute_key, param)
expect(redis).to receive(:set).with(redis_key, anything, any_args) expect(redis).to receive(:set).with(redis_key, anything, any_args)
end end
end end
end end
end
describe '#destroy' do describe '#destroy' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner) }
......
...@@ -409,7 +409,7 @@ describe API::Runner do ...@@ -409,7 +409,7 @@ describe API::Runner do
expect { request_job }.to change { runner.reload.contacted_at } expect { request_job }.to change { runner.reload.contacted_at }
end end
%w(name version revision platform architecture).each do |param| %w(version revision platform architecture).each do |param|
context "when info parameter '#{param}' is present" do context "when info parameter '#{param}' is present" do
let(:value) { "#{param}_value" } let(:value) { "#{param}_value" }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment