Commit aa81c19b authored by Pavel Shutsin's avatar Pavel Shutsin Committed by Adam Hegyi

Prepare devops adoption DB structure for migration

New structure will separate "display namespace" and
"target namespace" concepts for devops adoption

Changelog: added
parent 44c58cd6
---
title: Prepare devops adoption database structure for migration
merge_request: 60733
author:
type: other
# frozen_string_literal: true
class AddSnapshotNamespaceId < ActiveRecord::Migration[6.0]
def change
add_column :analytics_devops_adoption_snapshots, :namespace_id, :integer
end
end
# frozen_string_literal: true
class AddDisplayNamespaceIdToSegments < ActiveRecord::Migration[6.0]
def change
add_column :analytics_devops_adoption_segments, :display_namespace_id, :integer
end
end
# frozen_string_literal: true
class AddDevopsAdoptionIndexes < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
SEGMENTS_INDEX_NAME = 'idx_devops_adoption_segments_namespaces_pair'
SNAPSHOT_END_TIME_INDEX_NAME = 'idx_devops_adoption_segments_namespace_end_time'
SNAPSHOT_RECORDED_AT_INDEX_NAME = 'idx_devops_adoption_segments_namespace_recorded_at'
def up
add_concurrent_index :analytics_devops_adoption_snapshots, [:namespace_id, :end_time],
name: SNAPSHOT_END_TIME_INDEX_NAME
add_concurrent_index :analytics_devops_adoption_snapshots, [:namespace_id, :recorded_at],
name: SNAPSHOT_RECORDED_AT_INDEX_NAME
add_concurrent_index :analytics_devops_adoption_segments, [:display_namespace_id, :namespace_id],
unique: true, name: SEGMENTS_INDEX_NAME
add_concurrent_foreign_key :analytics_devops_adoption_snapshots, :namespaces, column: :namespace_id
add_concurrent_foreign_key :analytics_devops_adoption_segments, :namespaces, column: :display_namespace_id
end
def down
remove_foreign_key :analytics_devops_adoption_segments, :namespaces, column: :display_namespace_id
remove_foreign_key :analytics_devops_adoption_snapshots, :namespaces, column: :namespace_id
remove_concurrent_index_by_name :analytics_devops_adoption_segments, SEGMENTS_INDEX_NAME
remove_concurrent_index_by_name :analytics_devops_adoption_snapshots, SNAPSHOT_RECORDED_AT_INDEX_NAME
remove_concurrent_index_by_name :analytics_devops_adoption_snapshots, SNAPSHOT_END_TIME_INDEX_NAME
end
end
# frozen_string_literal: true
class RemoveObsoleteSegmentsField < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
with_lock_retries do
remove_column :analytics_devops_adoption_segments, :name
end
end
def down
add_column :analytics_devops_adoption_segments, :name, :text
add_text_limit :analytics_devops_adoption_segments, :name, 255
end
end
# frozen_string_literal: true
class CopyAdoptionSnapshotNamespace < ActiveRecord::Migration[6.0]
def up
execute <<-SQL
UPDATE analytics_devops_adoption_snapshots snapshots
SET namespace_id = segments.namespace_id
FROM analytics_devops_adoption_segments segments
WHERE snapshots.namespace_id IS NULL AND segments.id = snapshots.segment_id
SQL
end
def down
execute 'UPDATE analytics_devops_adoption_snapshots SET namespace_id = NULL'
end
end
# frozen_string_literal: true
class CopyAdoptionSegmentsNamespace < ActiveRecord::Migration[6.0]
def up
execute <<-SQL
UPDATE analytics_devops_adoption_segments SET display_namespace_id = namespace_id
WHERE display_namespace_id IS NULL
SQL
end
def down
execute 'UPDATE analytics_devops_adoption_segments SET display_namespace_id = NULL'
end
end
476dc70eae87ad3ee30e6be8c1afb4a2aec23a09b96daba2afbd9c4e2edb12b9
\ No newline at end of file
ebdeb56647f3a7ff5620141833c90b796a9ddfed39234bcf8063ca5b3df6c1f3
\ No newline at end of file
7f6862205e8c315da8433083fc5391f8889951f62d466e0048063322a46f9cc7
\ No newline at end of file
c4a4b214f15a1a8d7f6832782d50077189281ca9a9b1b746a0a3bc3af4a47e3c
\ No newline at end of file
77e2b8c1c6054a80122f97dda1e843149fefb7bf6694fdfa897d691d61162d81
\ No newline at end of file
c5fe6f74822168599ad5069bb7c793ec96a4bba99d15ad29cb161ef24291b56d
\ No newline at end of file
......@@ -9103,12 +9103,11 @@ ALTER SEQUENCE analytics_devops_adoption_segment_selections_id_seq OWNED BY anal
CREATE TABLE analytics_devops_adoption_segments (
id bigint NOT NULL,
name text,
last_recorded_at timestamp with time zone,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
namespace_id integer,
CONSTRAINT check_4be7a006fd CHECK ((char_length(name) <= 255))
display_namespace_id integer
);
CREATE SEQUENCE analytics_devops_adoption_segments_id_seq
......@@ -9133,7 +9132,8 @@ CREATE TABLE analytics_devops_adoption_snapshots (
security_scan_succeeded boolean NOT NULL,
end_time timestamp with time zone NOT NULL,
total_projects_count integer,
code_owners_used_count integer
code_owners_used_count integer,
namespace_id integer
);
CREATE SEQUENCE analytics_devops_adoption_snapshots_id_seq
......@@ -22133,6 +22133,12 @@ CREATE INDEX idx_container_repositories_on_exp_cleanup_status_and_start_date ON
CREATE INDEX idx_deployment_clusters_on_cluster_id_and_kubernetes_namespace ON deployment_clusters USING btree (cluster_id, kubernetes_namespace);
CREATE INDEX idx_devops_adoption_segments_namespace_end_time ON analytics_devops_adoption_snapshots USING btree (namespace_id, end_time);
CREATE INDEX idx_devops_adoption_segments_namespace_recorded_at ON analytics_devops_adoption_snapshots USING btree (namespace_id, recorded_at);
CREATE UNIQUE INDEX idx_devops_adoption_segments_namespaces_pair ON analytics_devops_adoption_segments USING btree (display_namespace_id, namespace_id);
CREATE INDEX idx_eaprpb_external_approval_rule_id ON external_approval_rules_protected_branches USING btree (external_approval_rule_id);
CREATE INDEX idx_elastic_reindexing_slices_on_elastic_reindexing_subtask_id ON elastic_reindexing_slices USING btree (elastic_reindexing_subtask_id);
......@@ -25224,6 +25230,9 @@ ALTER TABLE ONLY project_features
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_190998ef09 FOREIGN KEY (external_pull_request_id) REFERENCES external_pull_requests(id) ON DELETE SET NULL;
ALTER TABLE ONLY analytics_devops_adoption_segments
ADD CONSTRAINT fk_190a24754d FOREIGN KEY (display_namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_details
ADD CONSTRAINT fk_190e4fcc88 FOREIGN KEY (provisioned_by_group_id) REFERENCES namespaces(id) ON DELETE SET NULL;
......@@ -25440,6 +25449,9 @@ ALTER TABLE ONLY users
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_78a6492f68 FOREIGN KEY (repository_updated_event_id) REFERENCES geo_repository_updated_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_devops_adoption_snapshots
ADD CONSTRAINT fk_78c9eac821 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY lists
ADD CONSTRAINT fk_7a5553d60f FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE;
......@@ -4,14 +4,19 @@ class Analytics::DevopsAdoption::Segment < ApplicationRecord
include IgnorableColumns
belongs_to :namespace
belongs_to :display_namespace, class_name: 'Namespace', optional: true
has_many :snapshots, inverse_of: :segment
has_one :latest_snapshot, -> { order(recorded_at: :desc) }, inverse_of: :segment, class_name: 'Snapshot'
ignore_column :name, remove_with: '13.11', remove_after: '2021-03-22'
ignore_column :name, remove_with: '14.0', remove_after: '2021-05-22'
validates :namespace, uniqueness: true, presence: true
scope :ordered_by_name, -> { includes(:namespace).order('"namespaces"."name" ASC') }
scope :for_namespaces, -> (namespaces) { where(namespace_id: namespaces) }
scope :for_parent, -> (namespace) { for_namespaces(namespace.self_and_descendants) }
# Remove in %14.0 with https://gitlab.com/gitlab-org/gitlab/-/issues/329521
before_validation -> { self.display_namespace = namespace }
end
......@@ -2,6 +2,7 @@
class Analytics::DevopsAdoption::Snapshot < ApplicationRecord
belongs_to :segment, inverse_of: :snapshots
belongs_to :namespace, optional: true
validates :segment, presence: true
validates :recorded_at, presence: true
......@@ -29,6 +30,9 @@ class Analytics::DevopsAdoption::Snapshot < ApplicationRecord
scope :for_month, -> (month_date) { where(end_time: month_date.end_of_month) }
scope :not_finalized, -> { where(arel_table[:recorded_at].lteq(arel_table[:end_time])) }
# Remove in %14.0 with https://gitlab.com/gitlab-org/gitlab/-/issues/329521
before_save -> { self.namespace = segment.namespace }
def start_time
end_time.beginning_of_month
end
......
......@@ -17,6 +17,16 @@ RSpec.describe Analytics::DevopsAdoption::Segment, type: :model do
it { is_expected.to validate_uniqueness_of(:namespace) }
end
describe '#display_namespace' do
subject { build(:devops_adoption_segment) }
it 'fills display_namespace with namespace on save' do
expect do
subject.save!
end.to change { subject.display_namespace }.to(subject.namespace)
end
end
describe '.ordered_by_name' do
subject(:segments) { described_class.ordered_by_name }
......
......@@ -57,4 +57,14 @@ RSpec.describe Analytics::DevopsAdoption::Snapshot, type: :model do
expect(segment.start_time).to eq(expected_start_time)
end
end
describe '#namespace' do
subject { build(:devops_adoption_snapshot) }
it 'fills namespace with segments namespace on save' do
expect do
subject.save!
end.to change { subject.namespace }.to(subject.segment.namespace)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20210430134202_copy_adoption_snapshot_namespace.rb')
RSpec.describe CopyAdoptionSnapshotNamespace, :migration do
let(:namespaces_table) { table(:namespaces) }
let(:segments_table) { table(:analytics_devops_adoption_segments) }
let(:snapshots_table) { table(:analytics_devops_adoption_snapshots) }
before do
namespaces_table.create!(id: 123, name: 'group1', path: 'group1')
namespaces_table.create!(id: 124, name: 'group2', path: 'group2')
segments_table.create!(id: 1, namespace_id: 123)
segments_table.create!(id: 2, namespace_id: 124)
create_snapshot(id: 1, segment_id: 1)
create_snapshot(id: 2, segment_id: 2)
create_snapshot(id: 3, segment_id: 2, namespace_id: 123)
end
it 'updates all snapshots without namespace set' do
migrate!
expect(snapshots_table.find(1).namespace_id).to eq 123
expect(snapshots_table.find(2).namespace_id).to eq 124
expect(snapshots_table.find(3).namespace_id).to eq 123
end
def create_snapshot(**additional_params)
defaults = {
recorded_at: Time.zone.now,
issue_opened: true,
merge_request_opened: true,
merge_request_approved: true,
runner_configured: true,
pipeline_succeeded: true,
deploy_succeeded: true,
security_scan_succeeded: true,
end_time: Time.zone.now.end_of_month
}
snapshots_table.create!(defaults.merge(additional_params))
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20210430135954_copy_adoption_segments_namespace.rb')
RSpec.describe CopyAdoptionSegmentsNamespace, :migration do
let(:namespaces_table) { table(:namespaces) }
let(:segments_table) { table(:analytics_devops_adoption_segments) }
before do
namespaces_table.create!(id: 123, name: 'group1', path: 'group1')
namespaces_table.create!(id: 124, name: 'group2', path: 'group2')
segments_table.create!(id: 1, namespace_id: 123, display_namespace_id: nil)
segments_table.create!(id: 2, namespace_id: 124, display_namespace_id: 123)
end
it 'updates all segments without display namespace' do
migrate!
expect(segments_table.find(1).display_namespace_id).to eq 123
expect(segments_table.find(2).display_namespace_id).to eq 123
end
end
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