Class: Ci::RunnerManager

Inherits:
ApplicationRecord show all
Includes:
HasRunnerExecutor, HasRunnerStatus, EachBatch, FromUnion, RedisCacheable
Defined in:
app/models/ci/runner_manager.rb

Constant Summary collapse

AVAILABLE_STATUSES =
%w[online offline never_contacted stale].freeze
AVAILABLE_STATUSES_INCL_DEPRECATED =
AVAILABLE_STATUSES
UPDATE_CONTACT_COLUMN_EVERY =

The UPDATE_CONTACT_COLUMN_EVERY defines how often the Runner Machine DB entry can be updated

(40.minutes)..(55.minutes)
EXECUTOR_NAME_TO_TYPES =
{
  'unknown' => :unknown,
  'custom' => :custom,
  'shell' => :shell,
  'docker' => :docker,
  'docker-windows' => :docker_windows,
  'docker-ssh' => :docker_ssh,
  'ssh' => :ssh,
  'parallels' => :parallels,
  'virtualbox' => :virtualbox,
  'docker+machine' => :docker_machine,
  'docker-ssh+machine' => :docker_ssh_machine,
  'kubernetes' => :kubernetes,
  'docker-autoscaler' => :docker_autoscaler,
  'instance' => :instance
}.freeze
EXECUTOR_TYPE_TO_NAMES =
EXECUTOR_NAME_TO_TYPES.invert.freeze
STALE_TIMEOUT =

The STALE_TIMEOUT constant defines the how far past the last contact or creation date a runner manager will be considered stale

7.days

Constants included from RedisCacheable

RedisCacheable::CACHED_ATTRIBUTES_EXPIRY_TIME

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HasRunnerStatus

#online?, #stale?, #status

Methods included from RedisCacheable

#cache_attributes, #cached_attribute, #merge_cache_attributes

Methods inherited from ApplicationRecord

model_name, table_name_prefix

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.aggregate_upgrade_status_by_runner_idObject



133
134
135
136
137
138
# File 'app/models/ci/runner_manager.rb', line 133

def self.aggregate_upgrade_status_by_runner_id
  joins(:runner_version)
    .group(:runner_id)
    .maximum(:status)
    .transform_values { |s| Ci::RunnerVersion.statuses.key(s).to_sym }
end

.ip_address_exists?(ip_address) ⇒ Boolean

Returns:

  • (Boolean)


140
141
142
# File 'app/models/ci/runner_manager.rb', line 140

def self.ip_address_exists?(ip_address)
  exists?(ip_address:)
end

.online_contact_time_deadlineObject



125
126
127
# File 'app/models/ci/runner_manager.rb', line 125

def self.online_contact_time_deadline
  Ci::Runner.online_contact_time_deadline
end

.stale_deadlineObject



129
130
131
# File 'app/models/ci/runner_manager.rb', line 129

def self.stale_deadline
  STALE_TIMEOUT.ago
end

.version_regex_expression_for_version(version) ⇒ Object



144
145
146
147
148
149
150
151
152
153
# File 'app/models/ci/runner_manager.rb', line 144

def self.version_regex_expression_for_version(version)
  case version
  when /\d+\.\d+\.\d+/
    '^\d+\.\d+\.\d+'
  when /\d+\.\d+(\.)?/
    '^\d+\.\d+\.'
  else
    '^\d+\.'
  end
end

Instance Method Details

#heartbeat(values) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'app/models/ci/runner_manager.rb', line 159

def heartbeat(values)
  ##
  # We can safely ignore writes performed by a runner heartbeat. We do
  # not want to upgrade database connection proxy to use the primary
  # database after heartbeat write happens.
  #
  ::Gitlab::Database::LoadBalancing::SessionMap.current(load_balancer).without_sticky_writes do
    values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config,
      :executor, :runtime_features, :labels) || {}

    values.merge!(contacted_at: Time.current, creation_state: :finished)

    if values.include?(:executor)
      values[:executor_type] = EXECUTOR_NAME_TO_TYPES.fetch(values.delete(:executor), :unknown)
    end

    new_version = values[:version]
    schedule_runner_version_update(new_version) if new_version && new_version != version

    merge_cache_attributes(values)

    # We save data without validation, it will always change due to `contacted_at`
    update_columns(values) if persist_cached_data?
  end
end

#supports_after_script_on_cancel?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'app/models/ci/runner_manager.rb', line 198

def supports_after_script_on_cancel?
  !!runtime_features['cancel_gracefully']
end

#uncached_contacted_atObject



155
156
157
# File 'app/models/ci/runner_manager.rb', line 155

def uncached_contacted_at
  read_attribute(:contacted_at)
end

#update_columns(attrs = {}) ⇒ Object

While using update_columns may be useful from performance perspective, we still want the updated_at column to be updated when actual runner attributes change. However, we don't want to update it when only contacted_at changes, as this would cause unnecessary incremental sync operations in downstream systems (e.g., Snowflake).



189
190
191
192
193
194
195
196
# File 'app/models/ci/runner_manager.rb', line 189

def update_columns(attrs = {})
  # Only update columns that actually changed
  attrs_to_update = changed_values(attrs)

  attrs_to_update[:updated_at] = Time.current if attrs_to_update.except(:contacted_at, :creation_state).any?

  super(attrs_to_update) if attrs_to_update.any?
end