From da680b966c90c85eb4dd728e4433269616ed1883 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 22 Oct 2025 18:35:05 +0200 Subject: [PATCH] Bump updated_at on Ci::RunnerManager#heartbeat As per the discussion at https://gitlab.com/gitlab-data/analytics/-/issues/25129#note_2835760103 we found by using `update_columns()` we don't bump the `updated_at` column of the `ci_runner_machines` table. Which is really confusing and makes external mechanisms, like our data analyzis platform feeding mechanism, not catching the updates that were happening. --- app/models/ci/runner_manager.rb | 9 +++++++++ spec/models/ci/runner_manager_spec.rb | 12 +++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/models/ci/runner_manager.rb b/app/models/ci/runner_manager.rb index b9db19b9a1fc35..aa470aa0d7abf5 100644 --- a/app/models/ci/runner_manager.rb +++ b/app/models/ci/runner_manager.rb @@ -184,6 +184,15 @@ def heartbeat(values) end end + # While using update_columns may be useful from performance perspective, we still want + # the `updated_at` column to be updated. As most updates to the Ci::RunnerManager entity + # happen through the heartbeat model, when receiving some new/updated details from the + # runner side and without this change, `updated_at` may equal to `created_at` even months + # later, while the record did get multiple changes in that time. + def update_columns(attrs = {}) + super(attrs.reverse_merge(updated_at: Time.current)) + end + def supports_after_script_on_cancel? !!runtime_features['cancel_gracefully'] end diff --git a/spec/models/ci/runner_manager_spec.rb b/spec/models/ci/runner_manager_spec.rb index 0235ff0005853f..58d3b7fa173c61 100644 --- a/spec/models/ci/runner_manager_spec.rb +++ b/spec/models/ci/runner_manager_spec.rb @@ -514,7 +514,7 @@ end describe '#heartbeat', :freeze_time do - let(:runner_manager) { create(:ci_runner_machine, version: '15.0.0') } + let(:runner_manager) { create(:ci_runner_machine, version: '15.0.0', updated_at: (Time.current - 1.day)) } let(:executor) { 'shell' } let(:values) do { @@ -658,6 +658,7 @@ expect_redis_update expect { heartbeat }.to change { runner_manager.reload.read_attribute(:labels) } .from({}).to({ 'environment' => 'production', 'team' => 'backend' }) + .and change { runner_manager.reload.read_attribute(:updated_at) } end end end @@ -666,7 +667,7 @@ let(:version) { runner_manager.version } it 'does not schedule ci_runner_versions update' do - heartbeat + expect { heartbeat }.to change { runner_manager.reload.read_attribute(:updated_at) } expect(Ci::Runners::ProcessRunnerVersionUpdateWorker).not_to have_received(:perform_async) end @@ -678,7 +679,7 @@ it 'updates with expected executor type' do expect_redis_update - heartbeat + expect { heartbeat }.to change { runner_manager.reload.read_attribute(:updated_at) } expect(runner_manager.reload.read_attribute(:executor_type)).to eq(expected_executor_type) end @@ -695,7 +696,7 @@ def expected_executor_type it 'updates with unknown executor type' do expect_redis_update - heartbeat + expect { heartbeat }.to change { runner_manager.reload.read_attribute(:updated_at) } expect(runner_manager.reload.read_attribute(:executor_type)).to eq('unknown') end @@ -709,7 +710,7 @@ def expected_executor_type it 'updates labels' do expect_redis_update - heartbeat + expect { heartbeat }.to change { runner_manager.reload.read_attribute(:updated_at) } expect(runner_manager.reload.read_attribute(:labels)).to eq(values[:labels]) end @@ -732,6 +733,7 @@ def does_db_update .and change { runner_manager.reload.read_attribute(:config) } .and change { runner_manager.reload.read_attribute(:executor_type) } .and change { runner_manager.reload.read_attribute(:runtime_features) } + .and change { runner_manager.reload.read_attribute(:updated_at) } end end -- GitLab