summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
downloadgitlab-ce-8c890596f5d0792c467fe12805ab1b39f93bf140.tar.gz
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/lib/gitlab/database/partitioning/partition_manager_spec.rb')
-rw-r--r--spec/lib/gitlab/database/partitioning/partition_manager_spec.rb143
1 files changed, 136 insertions, 7 deletions
diff --git a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
index 903a41d6dd2..3d60457c3a9 100644
--- a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
@@ -4,9 +4,14 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
include Database::PartitioningHelpers
- include Database::TableSchemaHelpers
include ExclusiveLeaseHelpers
+ def has_partition(model, month)
+ Gitlab::Database::PostgresPartition.for_parent_table(model.table_name).any? do |partition|
+ Gitlab::Database::Partitioning::TimePartition.from_sql(model.table_name, partition.name, partition.condition).from == month
+ end
+ end
+
describe '.register' do
let(:model) { double(partitioning_strategy: nil) }
@@ -111,14 +116,14 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
let(:extra_partitions) do
[
- instance_double(Gitlab::Database::Partitioning::TimePartition, table: table, partition_name: 'foo1'),
- instance_double(Gitlab::Database::Partitioning::TimePartition, table: table, partition_name: 'foo2')
+ instance_double(Gitlab::Database::Partitioning::TimePartition, table: table, partition_name: 'foo1', to_detach_sql: 'SELECT 1'),
+ instance_double(Gitlab::Database::Partitioning::TimePartition, table: table, partition_name: 'foo2', to_detach_sql: 'SELECT 2')
]
end
- context 'with the partition_pruning_dry_run feature flag enabled' do
+ context 'with the partition_pruning feature flag enabled' do
before do
- stub_feature_flags(partition_pruning_dry_run: true)
+ stub_feature_flags(partition_pruning: true)
end
it 'detaches each extra partition' do
@@ -146,9 +151,9 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
end
- context 'with the partition_pruning_dry_run feature flag disabled' do
+ context 'with the partition_pruning feature flag disabled' do
before do
- stub_feature_flags(partition_pruning_dry_run: false)
+ stub_feature_flags(partition_pruning: false)
end
it 'returns immediately' do
@@ -158,4 +163,128 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
end
end
+
+ describe '#detach_partitions' do
+ around do |ex|
+ travel_to(Date.parse('2021-06-23')) do
+ ex.run
+ end
+ end
+
+ subject { described_class.new([my_model]).sync_partitions }
+
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:my_model) do
+ Class.new(ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = 'my_model_example_table'
+
+ partitioned_by :created_at, strategy: :monthly, retain_for: 1.month
+ end
+ end
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE my_model_example_table
+ (id serial not null, created_at timestamptz not null, primary key (id, created_at))
+ PARTITION BY RANGE (created_at);
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.my_model_example_table_202104
+ PARTITION OF my_model_example_table
+ FOR VALUES FROM ('2021-04-01') TO ('2021-05-01');
+
+ CREATE TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.my_model_example_table_202105
+ PARTITION OF my_model_example_table
+ FOR VALUES FROM ('2021-05-01') TO ('2021-06-01');
+ SQL
+
+ # Also create all future partitions so that the sync is only trying to detach old partitions
+ my_model.partitioning_strategy.missing_partitions.each do |p|
+ connection.execute p.to_sql
+ end
+ end
+
+ def num_tables
+ connection.select_value(<<~SQL)
+ SELECT COUNT(*)
+ FROM pg_class
+ where relkind IN ('r', 'p')
+ SQL
+ end
+
+ it 'detaches exactly one partition' do
+ expect { subject }.to change { find_partitions(my_model.table_name, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA).size }.from(9).to(8)
+ end
+
+ it 'detaches the old partition' do
+ expect { subject }.to change { has_partition(my_model, 2.months.ago.beginning_of_month) }.from(true).to(false)
+ end
+
+ it 'deletes zero tables' do
+ expect { subject }.not_to change { num_tables }
+ end
+
+ it 'creates the appropriate PendingPartitionDrop entry' do
+ subject
+
+ pending_drop = Postgresql::DetachedPartition.find_by!(table_name: 'my_model_example_table_202104')
+ expect(pending_drop.drop_after).to eq(Time.current + described_class::RETAIN_DETACHED_PARTITIONS_FOR)
+ end
+
+ # Postgres 11 does not support foreign keys to partitioned tables
+ if Gitlab::Database.main.version.to_f >= 12
+ context 'when the model is the target of a foreign key' do
+ before do
+ connection.execute(<<~SQL)
+ create unique index idx_for_fk ON my_model_example_table(created_at);
+
+ create table referencing_table (
+ id bigserial primary key not null,
+ referencing_created_at timestamptz references my_model_example_table(created_at)
+ );
+ SQL
+ end
+
+ it 'does not detach partitions with a referenced foreign key' do
+ expect { subject }.not_to change { find_partitions(my_model.table_name).size }
+ end
+ end
+ end
+ end
+
+ context 'creating and then detaching partitions for a table' do
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:my_model) do
+ Class.new(ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = 'my_model_example_table'
+
+ partitioned_by :created_at, strategy: :monthly, retain_for: 1.month
+ end
+ end
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE TABLE my_model_example_table
+ (id serial not null, created_at timestamptz not null, primary key (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
+
+ def num_partitions(model)
+ find_partitions(model.table_name, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA).size
+ end
+
+ it 'creates partitions for the future then drops the oldest one after a month' do
+ # 1 month for the current month, 1 month for the old month that we're retaining data for, headroom
+ expected_num_partitions = (Gitlab::Database::Partitioning::MonthlyStrategy::HEADROOM + 2.months) / 1.month
+ expect { described_class.new([my_model]).sync_partitions }.to change { num_partitions(my_model) }.from(0).to(expected_num_partitions)
+
+ travel 1.month
+
+ expect { described_class.new([my_model]).sync_partitions }.to change { has_partition(my_model, 2.months.ago.beginning_of_month) }.from(true).to(false).and(change { num_partitions(my_model) }.by(0))
+ end
+ end
end