diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-01 09:09:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-01 09:09:00 +0300 |
commit | dbc554cda7f86f22d34fd1e3a4ec4ab677d55879 (patch) | |
tree | e4a8f34cf3e1494b29c1eaaa021ea0e1dfc8f7e5 /app/models | |
parent | a3c1c29aa588228a2ae9c000620d1b73ff159fe3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/concerns/loose_foreign_key.rb | 95 | ||||
-rw-r--r-- | app/models/environment.rb | 41 |
2 files changed, 100 insertions, 36 deletions
diff --git a/app/models/concerns/loose_foreign_key.rb b/app/models/concerns/loose_foreign_key.rb new file mode 100644 index 00000000000..4e822a04869 --- /dev/null +++ b/app/models/concerns/loose_foreign_key.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module LooseForeignKey + extend ActiveSupport::Concern + + # This concern adds loose foreign key support to ActiveRecord models. + # Loose foreign keys allow delayed processing of associated database records + # with similar guarantees than a database foreign key. + # + # TODO: finalize this later once the async job is in place + # + # Prerequisites: + # + # To start using the concern, you'll need to install a database trigger to the parent + # table in a standard DB migration (not post-migration). + # + # > add_loose_foreign_key_support(:projects, :gitlab_main) + # + # Usage: + # + # > class Ci::Build < ApplicationRecord + # > + # > loose_foreign_key :security_scans, :build_id, on_delete: :async_delete, gitlab_schema: :gitlab_main + # > + # > # associations can be still defined, the dependent options is no longer necessary: + # > has_many :security_scans, class_name: 'Security::Scan' + # > + # > end + # + # Options for on_delete: + # + # - :async_delete - deletes the children rows via an asynchronous process. + # - :async_nullify - sets the foreign key column to null via an asynchronous process. + # + # Options for gitlab_schema: + # + # - :gitlab_ci + # - :gitlab_main + # + # The value can be determined by calling `Model.gitlab_schema` where the Model represents + # the model for the child table. + # + # How it works: + # + # When adding loose foreign key support to the table, a DELETE trigger is installed + # which tracks the record deletions (stores primary key value of the deleted row) in + # a database table. + # + # These deletion records are processed asynchronously and records are cleaned up + # according to the loose foreign key definitions described in the model. + # + # The cleanup happens in batches, which reduces the likelyhood of statement timeouts. + # + # When all associations related to the deleted record are cleaned up, the record itself + # is deleted. + included do + class_attribute :loose_foreign_key_definitions, default: [] + end + + class_methods do + def loose_foreign_key(to_table, column, options) + symbolized_options = options.symbolize_keys + + unless base_class? + raise <<~MSG + loose_foreign_key can be only used on base classes, inherited classes are not supported. + Please define the loose_foreign_key on the #{base_class.name} class. + MSG + end + + on_delete_options = %i[async_delete async_nullify] + gitlab_schema_options = [ApplicationRecord.gitlab_schema, Ci::ApplicationRecord.gitlab_schema] + + unless on_delete_options.include?(symbolized_options[:on_delete]&.to_sym) + raise "Invalid on_delete option given: #{symbolized_options[:on_delete]}. Valid options: #{on_delete_options.join(', ')}" + end + + unless gitlab_schema_options.include?(symbolized_options[:gitlab_schema]&.to_sym) + raise "Invalid gitlab_schema option given: #{symbolized_options[:gitlab_schema]}. Valid options: #{gitlab_schema_options.join(', ')}" + end + + definition = ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new( + table_name.to_s, + to_table.to_s, + { + column: column.to_s, + on_delete: symbolized_options[:on_delete].to_sym, + gitlab_schema: symbolized_options[:gitlab_schema].to_sym + } + ) + + self.loose_foreign_key_definitions += [definition] + end + end +end diff --git a/app/models/environment.rb b/app/models/environment.rb index 963249c018a..a4084565579 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -77,6 +77,7 @@ class Environment < ApplicationRecord scope :in_review_folder, -> { where(environment_type: "review") } scope :for_name, -> (name) { where(name: name) } scope :preload_cluster, -> { preload(last_deployment: :cluster) } + scope :preload_project, -> { preload(:project) } scope :auto_stoppable, -> (limit) { available.where('auto_stop_at < ?', Time.zone.now).limit(limit) } scope :auto_deletable, -> (limit) { stopped.where('auto_delete_at < ?', Time.zone.now).limit(limit) } @@ -132,6 +133,10 @@ class Environment < ApplicationRecord state :available state :stopped + before_transition any => :stopped do |environment| + environment.auto_stop_at = nil + end + after_transition do |environment| environment.expire_etag_cache end @@ -168,33 +173,6 @@ class Environment < ApplicationRecord end class << self - ## - # This method returns stop actions (jobs) for multiple environments within one - # query. It's useful to avoid N+1 problem. - # - # NOTE: The count of environments should be small~medium (e.g. < 5000) - def stop_actions - cte = cte_for_deployments_with_stop_action - ci_builds = Ci::Build.arel_table - - inner_join_stop_actions = ci_builds.join(cte.table).on( - ci_builds[:project_id].eq(cte.table[:project_id]) - .and(ci_builds[:ref].eq(cte.table[:ref])) - .and(ci_builds[:name].eq(cte.table[:on_stop])) - ).join_sources - - pipeline_ids = ci_builds.join(cte.table).on( - ci_builds[:id].eq(cte.table[:deployable_id]) - ).project(:commit_id) - - Ci::Build.joins(inner_join_stop_actions) - .with(cte.to_arel) - .where(ci_builds[:commit_id].in(pipeline_ids)) - .where(status: Ci::HasStatus::BLOCKED_STATUS) - .preload_project_and_pipeline_project - .preload(:user, :metadata, :deployment) - end - def count_by_state environments_count_by_state = group(:state).count @@ -202,15 +180,6 @@ class Environment < ApplicationRecord count_hash[state] = environments_count_by_state[state.to_s] || 0 end end - - private - - def cte_for_deployments_with_stop_action - Gitlab::SQL::CTE.new(:deployments_with_stop_action, - Deployment.where(environment_id: select(:id)) - .distinct_on_environment - .stoppable) - end end def clear_prometheus_reactive_cache!(query_name) |