Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-11 00:10:03 +00:00
parent caff5659c9
commit d229251151
47 changed files with 988 additions and 191 deletions

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class CustomerRelations::Organization < ApplicationRecord
self.table_name = "customer_relations_organizations"
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'group_id'
before_validation :strip_whitespace!
enum state: {
inactive: 0,
active: 1
}
validates :group, presence: true
validates :name, presence: true
validates :name, uniqueness: { case_sensitive: false, scope: [:group_id] }
validates :name, length: { maximum: 255 }
validates :description, length: { maximum: 1024 }
def self.find_by_name(group_id, name)
where(group: group_id)
.where('LOWER(name) = LOWER(?)', name)
end
private
def strip_whitespace!
name&.strip!
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module Postgresql
class DetachedPartition < ApplicationRecord
scope :ready_to_drop, -> { where('drop_after < ?', Time.current) }
end
end

View File

@ -247,6 +247,15 @@
:idempotent: true
:tags:
- :exclude_from_kubernetes
- :name: cronjob:database_drop_detached_partitions
:worker_name: Database::DropDetachedPartitionsWorker
:feature_category: :database
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: cronjob:database_partition_management
:worker_name: Database::PartitionManagementWorker
:feature_category: :database

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Database
class DropDetachedPartitionsWorker
include ApplicationWorker
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
feature_category :database
data_consistency :always
idempotent!
def perform
Gitlab::Database::Partitioning::DetachedPartitionDropper.new.perform
ensure
Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
end
end
end

View File

@ -0,0 +1,8 @@
---
name: drop_detached_partitions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67056
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337155
milestone: '14.2'
type: development
group: group::database
default_enabled: false

View File

@ -1,8 +1,8 @@
---
name: partition_pruning_dry_run
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65093
rollout_issue_url:
milestone: '14.1'
name: partition_pruning
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67056
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337153
milestone: '14.2'
type: development
group: group::database
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: usage_data_i_testing_full_code_quality_report_total
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49079
rollout_issue_url:
milestone: '13.8'
type: development
group: group::testing
default_enabled: true

View File

@ -547,6 +547,9 @@ Settings.cron_jobs['update_container_registry_info_worker']['job_class'] = 'Upda
Settings.cron_jobs['postgres_dynamic_partitions_manager'] ||= Settingslogic.new({})
Settings.cron_jobs['postgres_dynamic_partitions_manager']['cron'] ||= '21 */6 * * *'
Settings.cron_jobs['postgres_dynamic_partitions_manager']['job_class'] ||= 'Database::PartitionManagementWorker'
Settings.cron_jobs['postgres_dynamic_partitions_dropper'] ||= Settingslogic.new({})
Settings.cron_jobs['postgres_dynamic_partitions_dropper']['cron'] ||= '45 12 * * *'
Settings.cron_jobs['postgres_dynamic_partitions_dropper']['job_class'] ||= 'Database::DropDetachedPartitionsWorker'
Settings.cron_jobs['ci_platform_metrics_update_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['cron'] ||= '47 9 * * *'
Settings.cron_jobs['ci_platform_metrics_update_cron_worker']['job_class'] = 'CiPlatformMetricsUpdateCronWorker'

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class CreateDetachedPartitionsTable < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def change
create_table_with_constraints :detached_partitions do |t|
t.timestamps_with_timezone null: false
t.datetime_with_timezone :drop_after, null: false
t.text :table_name, null: false
# Postgres identifier names can be up to 63 bytes
# See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
t.text_limit :table_name, 63
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class CreateCustomerRelationsOrganizations < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
def up
create_table_with_constraints :customer_relations_organizations do |t|
t.references :group, index: false, null: false, foreign_key: { to_table: :namespaces, on_delete: :cascade }
t.timestamps_with_timezone null: false
t.integer :state, limit: 1, default: 1, null: false
t.decimal :default_rate, precision: 18, scale: 2
t.text :name, null: false
t.text :description
t.text_limit :name, 255
t.text_limit :description, 1024
t.index 'group_id, LOWER(name)', unique: true, name: :index_customer_relations_organizations_on_unique_name_per_group
end
end
def down
with_lock_retries do
drop_table :customer_relations_organizations
end
end
end

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
class FinalizePushEventPayloadsBigintConversion3 < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
TABLE_NAME = 'push_event_payloads'
INDEX_NAME = 'index_push_event_payloads_on_event_id_convert_to_bigint'
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: TABLE_NAME,
column_name: 'event_id',
job_arguments: [["event_id"], ["event_id_convert_to_bigint"]]
)
return if already_swapped?
swap_columns
end
def down
swap_columns
end
private
def already_swapped?
push_event_payloads_columns = columns(TABLE_NAME)
event_id = push_event_payloads_columns.find {|c| c.name == 'event_id'}
event_id_convert_to_bigint = push_event_payloads_columns.find {|c| c.name == 'event_id_convert_to_bigint'}
event_id.sql_type == 'bigint' && event_id_convert_to_bigint.sql_type == 'integer'
end
def swap_columns
add_concurrent_index TABLE_NAME, :event_id_convert_to_bigint, unique: true, name: INDEX_NAME
# Add a foreign key on `event_id_convert_to_bigint` before we swap the columns and drop the old FK (fk_36c74129da)
add_concurrent_foreign_key TABLE_NAME, :events, column: :event_id_convert_to_bigint,
on_delete: :cascade, reverse_lock_order: true
with_lock_retries(raise_on_exhaustion: true) do
# We'll need ACCESS EXCLUSIVE lock on the related tables,
# lets make sure it can be acquired from the start.
# Lock order should be
# 1. events
# 2. push_event_payloads
# in order to match the order in EventCreateService#create_push_event,
# and avoid deadlocks.
execute "LOCK TABLE events, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
# Swap column names
temp_name = 'event_id_tmp'
execute "ALTER TABLE #{quote_table_name(TABLE_NAME)} RENAME COLUMN #{quote_column_name(:event_id)} TO #{quote_column_name(temp_name)}"
execute "ALTER TABLE #{quote_table_name(TABLE_NAME)} RENAME COLUMN #{quote_column_name(:event_id_convert_to_bigint)} TO #{quote_column_name(:event_id)}"
execute "ALTER TABLE #{quote_table_name(TABLE_NAME)} RENAME COLUMN #{quote_column_name(temp_name)} TO #{quote_column_name(:event_id_convert_to_bigint)}"
# We need to update the trigger function in order to make PostgreSQL to
# regenerate the execution plan for it. This is to avoid type mismatch errors like
# "type of parameter 15 (bigint) does not match that when preparing the plan (integer)"
function_name = Gitlab::Database::UnidirectionalCopyTrigger.on_table(TABLE_NAME).name(:event_id, :event_id_convert_to_bigint)
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
# Swap defaults
change_column_default TABLE_NAME, :event_id, nil
change_column_default TABLE_NAME, :event_id_convert_to_bigint, 0
# Swap PK constraint
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT push_event_payloads_pkey"
rename_index TABLE_NAME, INDEX_NAME, 'push_event_payloads_pkey'
execute "ALTER TABLE #{TABLE_NAME} ADD CONSTRAINT push_event_payloads_pkey PRIMARY KEY USING INDEX push_event_payloads_pkey"
# Drop original FK on the old int4 `event_id` (fk_36c74129da)
remove_foreign_key TABLE_NAME, name: concurrent_foreign_key_name(TABLE_NAME, :event_id)
# We swapped the columns but the FK for event_id is still using the old name for the event_id_convert_to_bigint column
# So we have to also swap the FK name now that we dropped the other one with the same
rename_constraint(
TABLE_NAME,
concurrent_foreign_key_name(TABLE_NAME, :event_id_convert_to_bigint),
concurrent_foreign_key_name(TABLE_NAME, :event_id)
)
end
end
end

View File

@ -0,0 +1 @@
136a375fbd7e1faf25e7f53e0677b8525811bd917892efa1430d204453bf2a1d

View File

@ -0,0 +1 @@
b844c7c56019fc984c2604ae11f6ee9eb587806b5c78e4beea4dda93e384f9b2

View File

@ -0,0 +1 @@
db62fb6413db4be5e1013bccf16b0c3a66c9aaf9f3d646f42442be16c511af5f

View File

@ -12058,6 +12058,28 @@ CREATE SEQUENCE custom_emoji_id_seq
ALTER SEQUENCE custom_emoji_id_seq OWNED BY custom_emoji.id;
CREATE TABLE customer_relations_organizations (
id bigint NOT NULL,
group_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
state smallint DEFAULT 1 NOT NULL,
default_rate numeric(18,2),
name text NOT NULL,
description text,
CONSTRAINT check_2ba9ef1c4c CHECK ((char_length(name) <= 255)),
CONSTRAINT check_e476b6058e CHECK ((char_length(description) <= 1024))
);
CREATE SEQUENCE customer_relations_organizations_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE customer_relations_organizations_id_seq OWNED BY customer_relations_organizations.id;
CREATE TABLE dast_profile_schedules (
id bigint NOT NULL,
project_id bigint NOT NULL,
@ -12521,6 +12543,24 @@ CREATE SEQUENCE design_user_mentions_id_seq
ALTER SEQUENCE design_user_mentions_id_seq OWNED BY design_user_mentions.id;
CREATE TABLE detached_partitions (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
drop_after timestamp with time zone NOT NULL,
table_name text NOT NULL,
CONSTRAINT check_aecee24ba3 CHECK ((char_length(table_name) <= 63))
);
CREATE SEQUENCE detached_partitions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE detached_partitions_id_seq OWNED BY detached_partitions.id;
CREATE TABLE diff_note_positions (
id bigint NOT NULL,
note_id bigint NOT NULL,
@ -17574,7 +17614,7 @@ ALTER SEQUENCE protected_tags_id_seq OWNED BY protected_tags.id;
CREATE TABLE push_event_payloads (
commit_count bigint NOT NULL,
event_id integer NOT NULL,
event_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
action smallint NOT NULL,
ref_type smallint NOT NULL,
commit_from bytea,
@ -17582,7 +17622,7 @@ CREATE TABLE push_event_payloads (
ref text,
commit_title character varying(70),
ref_count integer,
event_id_convert_to_bigint bigint DEFAULT 0 NOT NULL
event_id bigint NOT NULL
);
CREATE TABLE push_rules (
@ -20179,6 +20219,8 @@ ALTER TABLE ONLY csv_issue_imports ALTER COLUMN id SET DEFAULT nextval('csv_issu
ALTER TABLE ONLY custom_emoji ALTER COLUMN id SET DEFAULT nextval('custom_emoji_id_seq'::regclass);
ALTER TABLE ONLY customer_relations_organizations ALTER COLUMN id SET DEFAULT nextval('customer_relations_organizations_id_seq'::regclass);
ALTER TABLE ONLY dast_profile_schedules ALTER COLUMN id SET DEFAULT nextval('dast_profile_schedules_id_seq'::regclass);
ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profiles_id_seq'::regclass);
@ -20217,6 +20259,8 @@ ALTER TABLE ONLY design_management_versions ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY design_user_mentions ALTER COLUMN id SET DEFAULT nextval('design_user_mentions_id_seq'::regclass);
ALTER TABLE ONLY detached_partitions ALTER COLUMN id SET DEFAULT nextval('detached_partitions_id_seq'::regclass);
ALTER TABLE ONLY diff_note_positions ALTER COLUMN id SET DEFAULT nextval('diff_note_positions_id_seq'::regclass);
ALTER TABLE ONLY dora_daily_metrics ALTER COLUMN id SET DEFAULT nextval('dora_daily_metrics_id_seq'::regclass);
@ -21458,6 +21502,9 @@ ALTER TABLE ONLY csv_issue_imports
ALTER TABLE ONLY custom_emoji
ADD CONSTRAINT custom_emoji_pkey PRIMARY KEY (id);
ALTER TABLE ONLY customer_relations_organizations
ADD CONSTRAINT customer_relations_organizations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_profile_schedules
ADD CONSTRAINT dast_profile_schedules_pkey PRIMARY KEY (id);
@ -21533,6 +21580,9 @@ ALTER TABLE ONLY design_management_versions
ALTER TABLE ONLY design_user_mentions
ADD CONSTRAINT design_user_mentions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY detached_partitions
ADD CONSTRAINT detached_partitions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY diff_note_positions
ADD CONSTRAINT diff_note_positions_pkey PRIMARY KEY (id);
@ -23557,6 +23607,8 @@ CREATE INDEX index_custom_emoji_on_creator_id ON custom_emoji USING btree (creat
CREATE UNIQUE INDEX index_custom_emoji_on_namespace_id_and_name ON custom_emoji USING btree (namespace_id, name);
CREATE UNIQUE INDEX index_customer_relations_organizations_on_unique_name_per_group ON customer_relations_organizations USING btree (group_id, lower(name));
CREATE UNIQUE INDEX index_cycle_analytics_stage_event_hashes_on_hash_sha_256 ON analytics_cycle_analytics_stage_event_hashes USING btree (hash_sha256);
CREATE UNIQUE INDEX index_daily_build_group_report_results_unique_columns ON ci_daily_build_group_report_results USING btree (project_id, ref_path, date, group_name);
@ -27801,6 +27853,9 @@ ALTER TABLE ONLY jira_connect_subscriptions
ALTER TABLE ONLY fork_network_members
ADD CONSTRAINT fk_rails_a40860a1ca FOREIGN KEY (fork_network_id) REFERENCES fork_networks(id) ON DELETE CASCADE;
ALTER TABLE ONLY customer_relations_organizations
ADD CONSTRAINT fk_rails_a48597902f FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY operations_feature_flag_scopes
ADD CONSTRAINT fk_rails_a50a04d0a4 FOREIGN KEY (feature_flag_id) REFERENCES operations_feature_flags(id) ON DELETE CASCADE;

View File

@ -17,6 +17,7 @@ swap:
file name: filename
filesystem: file system
info: information
need to: must
repo: repository
timezone: time zone
utilize: use

View File

@ -107,7 +107,7 @@ You can customize the:
- SSH remote URL to use the location-aware `git.example.com`. To do so, change the SSH remote URL's
host by setting `gitlab_rails['gitlab_ssh_host']` in `gitlab.rb` of web nodes.
- HTTP remote URL as shown in
[Custom Git clone URL for HTTP(S)](../../../user/admin_area/settings/visibility_and_access_controls.md#custom-git-clone-url-for-https).
[Custom Git clone URL for HTTP(S)](../../../user/admin_area/settings/visibility_and_access_controls.md#customize-git-clone-url-for-https).
## Example Git request handling behavior

View File

@ -121,7 +121,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Creating users](../user/profile/account/create_accounts.md): Create users manually or through authentication integrations.
- [Libravatar](libravatar.md): Use Libravatar instead of Gravatar for user avatars.
- [Sign-up restrictions](../user/admin_area/settings/sign_up_restrictions.md): block email addresses of specific domains, or whitelist only specific domains.
- [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS).
- [Access restrictions](../user/admin_area/settings/visibility_and_access_controls.md#configure-enabled-git-access-protocols): Define which Git access protocols can be used to talk to GitLab (SSH, HTTP, HTTPS).
- [Authentication and Authorization](auth/index.md): Configure external authentication with LDAP, SAML, CAS, and additional providers.
- [Sync LDAP](auth/ldap/index.md)
- [Kerberos authentication](../integration/kerberos.md)

View File

@ -28,7 +28,7 @@ They are listed in the public access directory (`/public`) for all users.
NOTE:
By default, `/public` is visible to unauthenticated users. However, if the
[**Public** visibility level](../user/admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels)
[**Public** visibility level](../user/admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
is restricted, `/public` is visible only to signed-in users.
## Internal projects
@ -71,7 +71,7 @@ You can restrict the use of visibility levels for users when they create a proje
This is useful to prevent users from publicly exposing their repositories by accident. The
restricted visibility settings do not apply to admin users.
For details, see [Restricted visibility levels](../user/admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels).
For details, see [Restricted visibility levels](../user/admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels).
<!-- ## Troubleshooting

View File

@ -26,7 +26,7 @@ You can now see the message on `/help`.
NOTE:
By default, `/help` is visible to unauthenticated users. However, if the
[**Public** visibility level](visibility_and_access_controls.md#restricted-visibility-levels)
[**Public** visibility level](visibility_and_access_controls.md#restrict-visibility-levels)
is restricted, `/help` is visible only to signed-in users.
## Add a help message to the sign-in page

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -51,7 +51,7 @@ To access the default page for Admin Area settings:
| Option | Description |
| ------ | ----------- |
| [Repository's custom initial branch name](../../project/repository/branches/default.md#instance-level-custom-initial-branch-name) | Set a custom branch name for new repositories created in your instance. |
| [Repository mirror](visibility_and_access_controls.md#allow-mirrors-to-be-set-up-for-projects) | Configure repository mirroring. |
| [Repository mirror](visibility_and_access_controls.md#enable-project-mirroring) | Configure repository mirroring. |
| [Repository storage](../../../administration/repository_storage_types.md) | Configure storage path settings. |
| Repository maintenance | ([Repository checks](../../../administration/repository_checks.md) and [Housekeeping](../../../administration/housekeeping.md)). Configure automatic Git checks and housekeeping on repositories. |
| [Repository static objects](../../../administration/static_objects_external_storage.md) | Serve repository static objects (for example, archives and blobs) from an external storage (for example, a CDN). |

View File

@ -5,9 +5,10 @@ info: "To determine the technical writer assigned to the Stage/Group associated
type: reference
---
# Visibility and access controls **(FREE SELF)**
# Control access and visibility **(FREE SELF)**
GitLab allows administrators to enforce specific controls.
GitLab enables users with the [Administrator role](../../permissions.md) to enforce
specific controls on branches, projects, snippets, groups, and more.
To access the visibility and access control options:
@ -16,60 +17,84 @@ To access the visibility and access control options:
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
## Default branch protection
## Protect default branches
This global option defines the branch protection that applies to every repository's
[default branch](../../project/repository/branches/default.md).
[Branch protection](../../project/protected_branches.md) specifies which roles can push
to branches and which roles can delete branches. In this case _Default_ refers to a
repository's [default branch](../../project/repository/branches/default.md).
With this option, you can define [branch protections](../../project/protected_branches.md)
to apply to every repository's [default branch](../../project/repository/branches/default.md).
These protections specify the user roles with permission to:
This setting applies only to each repositories' default branch. To protect other branches, you must configure branch protection in repository. For details, see [protected branches](../../project/protected_branches.md).
- Push to branches.
- Delete branches.
To change the default branch protection:
This setting applies only to each repository's default branch. To protect other branches,
you must configure [branch protection in the repository](../../project/protected_branches.md),
or configure [branch protection for groups](../../group/index.md#change-the-default-branch-protection-of-a-group).
1. Select the desired option.
1. Click **Save changes**.
To change the default branch protection for the entire instance:
For more details, see [Protected branches](../../project/protected_branches.md).
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select a **Default branch protection**:
- **Not protected** - Both developers and maintainers can push new commits,
force push, or delete the branch.
- **Protected against pushes** - Developers cannot push new commits, but are
allowed to accept merge requests to the branch. Maintainers can push to the branch.
- **Partially protected** - Both developers and maintainers can push new commits,
but cannot force push or delete the branch.
- **Fully protected** - Developers cannot push new commits, but maintainers can.
No one can force push or delete the branch.
1. To allow group owners to override the instance's default branch protection, select
[**Allow owners to manage default branch protection per group**](#prevent-overrides-of-default-branch-protection).
1. Select **Save changes**.
To change this setting for a specific group, see [Default branch protection for groups](../../group/index.md#change-the-default-branch-protection-of-a-group)
### Disable group owners from updating default branch protection **(PREMIUM SELF)**
### Prevent overrides of default branch protection **(PREMIUM SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211944) in GitLab 13.0.
By default, group owners are allowed to override the branch protection set at the global level.
Instance-level protections for [default branch](../../project/repository/branches/default.md)
can be overridden on a per-group basis by the group's owner. In
[GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can
disable this privilege for group owners, enforcing the instance-level protection rule:
In [GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can disable this privilege of group owners.
To do this:
1. Uncheck the **Allow owners to manage default branch protection per group** checkbox.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Deselect the **Allow owners to manage default branch protection per group** checkbox.
1. Select **Save changes**.
NOTE:
GitLab administrators can still update the default branch protection of a group.
## Default project creation protection
## Define which roles can create projects
Project creation protection specifies which roles can create projects.
Instance-level protections for project creation define which roles can
[add projects to a group](../../group/index.md#specify-who-can-add-projects-to-a-group)]
on the instance. To alter which roles have permission to create projects:
To change the default project creation protection:
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. For **Default project creation protection**, select the desired roles:
- No one.
- Maintainers.
- Developers and Maintainers.
1. Select **Save changes**.
1. Select the desired option.
1. Click **Save changes**.
## Restrict project deletion to Administrators **(PREMIUM SELF)**
For more details, see [Specify who can add projects to a group](../../group/index.md#specify-who-can-add-projects-to-a-group).
Anyone with the **Owner** role, either at the project or group level, can
delete a project. To allow only users with the Administrator role to delete projects:
## Default project deletion protection **(PREMIUM SELF)**
By default, a project can be deleted by anyone with the **Owner** role, either at the project or
group level.
To ensure only Administrator users can delete projects:
1. Check the **Default project deletion protection** checkbox.
1. Click **Save changes**.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Scroll to **Default project deletion protection**, and select **Only admins can delete project**.
1. Select **Save changes**.
## Default delayed project deletion **(PREMIUM SELF)**
@ -104,7 +129,7 @@ To change this period:
1. Select the desired option.
1. Click **Save changes**.
### Override default deletion delayed period
### Override defaults and delete immediately
Alternatively, projects that are marked for removal can be deleted immediately. To do so:
@ -112,107 +137,132 @@ Alternatively, projects that are marked for removal can be deleted immediately.
1. Delete the project as described in the
[Administering Projects page](../../admin_area/#administering-projects).
## Default project visibility
## Configure project visibility defaults
To set the default visibility levels for new projects:
To set the default [visibility levels for new projects](../../../public_access/public_access.md):
1. Select the desired default project visibility.
1. Click **Save changes**.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default project visibility:
- **Private** - Project access must be granted explicitly to each user. If this
project is part of a group, access will be granted to members of the group.
- **Internal** - The project can be accessed by any logged in user except external users.
- **Public** - The project can be accessed without any authentication.
1. Select **Save changes**.
For more details on project visibility, see
[Project visibility](../../../public_access/public_access.md).
## Configure snippet visibility defaults
## Default snippet visibility
To set the default visibility levels for new snippets:
To set the default visibility levels for new [snippets](../../snippets.md):
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default snippet visibility.
1. Click **Save changes**.
1. Select **Save changes**.
For more details on snippet visibility, see
For more details on snippet visibility, read
[Project visibility](../../../public_access/public_access.md).
## Default group visibility
## Configure group visibility defaults
To set the default visibility levels for new groups:
1. Select the desired default group visibility.
1. Click **Save changes**.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default group visibility:
- **Private** - The group and its projects can only be viewed by members.
- **Internal** - The group and any internal projects can be viewed by any logged in user except external users.
- **Public** - The group and any public projects can be viewed without any authentication.
1. Select **Save changes**.
For more details on group visibility, see
[Group visibility](../../group/index.md#group-visibility).
## Restricted visibility levels
## Restrict visibility levels
To set the restricted visibility levels for projects, snippets, and selected pages:
To restrict visibility levels for projects, snippets, and selected pages:
1. Select the desired visibility levels to restrict.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. In the **Restricted visibility levels** section, select the desired visibility levels to restrict.
1. Select **Save changes**.
For more details on project visibility, see
[Project visibility](../../../public_access/public_access.md).
## Import sources
## Configure allowed import sources
To specify from which hosting sites users can [import their projects](../../project/import/index.md):
You can specify from which hosting sites users can [import their projects](../../project/import/index.md):
1. Check the checkbox beside the name of each hosting site.
1. Click **Save changes**.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select each of **Import sources** to allow.
1. Select **Save changes**.
## Project export
## Enable project export
To enable project export:
To enable the export of
[projects and their data](../../../user/project/settings/import_export.md#exporting-a-project-and-its-data):
1. Check the **Project export enabled** checkbox.
1. Click **Save changes**.
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select **Project export enabled**.
1. Select **Save changes**.
For more details, see [Exporting a project and its data](../../../user/project/settings/import_export.md#exporting-a-project-and-its-data).
## Configure enabled Git access protocols
## Enabled Git access protocols
With GitLab access restrictions, you can select with which protocols users can communicate with
GitLab.
Disabling an access protocol does not block access to the server itself by using those ports. The ports
used for the protocol, SSH or HTTP(S), are still accessible. The GitLab restrictions apply at the
application level.
With GitLab access restrictions, you can select the protocols users can use to
communicate with GitLab. Disabling an access protocol does not block port access to the
server itself. The ports used for the protocol, SSH or HTTP(S), are still accessible.
The GitLab restrictions apply at the application level.
To specify the enabled Git access protocols:
1. Select the desired Git access protocols from the dropdown:
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired Git access protocols:
- Both SSH and HTTP(S)
- Only SSH
- Only HTTP(S)
1. Click **Save changes**.
1. Select **Save changes**.
When both SSH and HTTP(S) are enabled, users can choose either protocol.
When only one protocol is enabled:
If only one protocol is enabled:
- The project page shows only the allowed protocol's URL, with no option to
change it.
- A tooltip is shown when you hover over the URL's protocol, if an action
on the user's part is required. For example, adding an SSH key or setting a password.
- GitLab shows a tooltip when you hover over the URL's protocol, if user action
(such as adding a SSH key or setting a password) is required:
![Project URL with SSH only access](img/restricted_url.png)
![Project URL with SSH only access](img/restricted_url.png)
On top of these UI restrictions, GitLab denies all Git actions on the protocol
not selected.
GitLab only allows Git actions for the protocols you select.
WARNING:
GitLab versions [10.7 and later](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18021),
allow the HTTP(S) protocol for Git clone or fetch requests done by GitLab Runner
from CI/CD jobs, even if **Only SSH** was selected.
from CI/CD jobs, even if you select **Only SSH**.
## Custom Git clone URL for HTTP(S)
## Customize Git clone URL for HTTP(S)
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18422) in GitLab 12.4.
You can customize project Git clone URLs for HTTP(S), which affects the clone
panel:
![Clone panel](img/clone_panel_v12_4.png)
For example, if:
- Your GitLab instance is at `https://example.com`, then project clone URLs are like
@ -231,7 +281,7 @@ NOTE:
SSH clone URLs can be customized in `gitlab.rb` by setting `gitlab_rails['gitlab_ssh_host']` and
other related settings.
## RSA, DSA, ECDSA, ED25519 SSH keys
## Configure defaults for RSA, DSA, ECDSA, ED25519 SSH keys
These options specify the permitted types and lengths for SSH keys.
@ -242,7 +292,7 @@ To specify a restriction for each key type:
For more details, see [SSH key restrictions](../../../security/ssh_keys_restrictions.md).
## Allow mirrors to be set up for projects
## Enable project mirroring
This option is enabled by default. By disabling it, both
[pull and push mirroring](../../project/repository/repository_mirroring.md) no longer

View File

@ -37,7 +37,7 @@ Like projects, a group can be configured to limit the visibility of it to:
- All signed-in users.
- Only explicit group members.
The restriction for [visibility levels](../admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels)
The restriction for [visibility levels](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
on the application setting level also applies to groups. If set to internal, the explore page is
empty for anonymous users. The group page has a visibility level icon.
@ -220,10 +220,10 @@ To change this setting for a specific group:
1. Select the desired option in the **Default branch protection** dropdown list.
1. Click **Save changes**.
To change this setting globally, see [Default branch protection](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
To change this setting globally, see [Default branch protection](../admin_area/settings/visibility_and_access_controls.md#protect-default-branches).
NOTE:
In [GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can choose to [disable group owners from updating the default branch protection](../admin_area/settings/visibility_and_access_controls.md#disable-group-owners-from-updating-default-branch-protection).
In [GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can choose to [disable group owners from updating the default branch protection](../admin_area/settings/visibility_and_access_controls.md#prevent-overrides-of-default-branch-protection).
## Add projects to a group
@ -248,7 +248,7 @@ To change this setting for a specific group:
1. Select the desired option in the **Allowed to create projects** dropdown list.
1. Click **Save changes**.
To change this setting globally, see [Default project creation protection](../admin_area/settings/visibility_and_access_controls.md#default-project-creation-protection).
To change this setting globally, see [Default project creation protection](../admin_area/settings/visibility_and_access_controls.md#define-which-roles-can-create-projects).
## Group activity analytics **(PREMIUM)**

View File

@ -330,7 +330,7 @@ The following table lists group permissions available for each role:
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
1. Introduced in GitLab 12.2.
1. Default project creation role can be changed at:
- The [instance level](admin_area/settings/visibility_and_access_controls.md#default-project-creation-protection).
- The [instance level](admin_area/settings/visibility_and_access_controls.md#define-which-roles-can-create-projects).
- The [group level](group/index.md#specify-who-can-add-projects-to-a-group).
1. Does not apply to subgroups.
1. Developers can push commits to the default branch of a new project only if the [default branch protection](group/index.md#change-the-default-branch-protection-of-a-group) is set to "Partially protected" or "Not protected".

View File

@ -86,7 +86,7 @@ not.
When visiting the public page of a user, you can only see the projects which you have privileges to.
If the [public level is restricted](../admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels),
If the [public level is restricted](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels),
user profiles are only visible to signed-in users.
## Add external accounts to your user profile page

View File

@ -36,4 +36,4 @@ of the project being imported into, then the user will be linked.
## Enable this feature
Enable Phabricator as an [import source](../../admin_area/settings/visibility_and_access_controls.md#import-sources) in the Admin Area.
Enable Phabricator as an [import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) in the Admin Area.

View File

@ -30,7 +30,7 @@ When a branch is protected, the default behavior enforces these restrictions on
### Set the default branch protection level
Administrators can set a default branch protection level in the
[Admin Area](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
[Admin Area](../admin_area/settings/visibility_and_access_controls.md#protect-default-branches).
## Configure a protected branch

View File

@ -22,7 +22,7 @@ projects with the largest number of comments in the past month, click **Trending
NOTE:
By default, `/explore` is visible to unauthenticated users. However, if the
[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels)
[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
is restricted, `/explore` is visible only to signed-in users.
## Create a project

View File

@ -341,9 +341,9 @@ module Gitlab
# - Per connection (requires a cleanup after the execution)
#
# When using a per connection disable statement, code must be inside
# a block so we can automatically execute `RESET ALL` after block finishes
# a block so we can automatically execute `RESET statement_timeout` after block finishes
# otherwise the statement will still be disabled until connection is dropped
# or `RESET ALL` is executed
# or `RESET statement_timeout` is executed
def disable_statement_timeout
if block_given?
if statement_timeout_disabled?
@ -357,7 +357,7 @@ module Gitlab
yield
ensure
execute('RESET ALL')
execute('RESET statement_timeout')
end
end
else

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
module Gitlab
module Database
module Partitioning
class DetachedPartitionDropper
def perform
return unless Feature.enabled?(:drop_detached_partitions, default_enabled: :yaml)
Gitlab::AppLogger.info(message: "Checking for previously detached partitions to drop")
Postgresql::DetachedPartition.ready_to_drop.find_each do |detached_partition|
conn.transaction do
# Another process may have already dropped the table and deleted this entry
next unless (detached_partition = Postgresql::DetachedPartition.lock.find_by(id: detached_partition.id))
unless check_partition_detached?(detached_partition)
Gitlab::AppLogger.error(message: "Attempt to drop attached database partition", partition_name: detached_partition.table_name)
detached_partition.destroy!
next
end
drop_one(detached_partition)
end
rescue StandardError => e
Gitlab::AppLogger.error(message: "Failed to drop previously detached partition",
partition_name: detached_partition.table_name,
exception_class: e.class,
exception_message: e.message)
end
end
private
def drop_one(detached_partition)
conn.transaction do
conn.execute(<<~SQL)
DROP TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{conn.quote_table_name(detached_partition.table_name)}
SQL
detached_partition.destroy!
end
Gitlab::AppLogger.info(message: "Dropped previously detached partition", partition_name: detached_partition.table_name)
end
def check_partition_detached?(detached_partition)
# PostgresPartition checks the pg_inherits view, so our partition will only show here if it's still attached
# and thus should not be dropped
!PostgresPartition.for_identifier("#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{detached_partition.table_name}").exists?
end
def conn
@conn ||= ApplicationRecord.connection
end
end
end
end
end

View File

@ -86,7 +86,7 @@ module Gitlab
end
def pruning_old_partitions?
Feature.enabled?(:partition_pruning_dry_run) && retain_for.present?
retain_for.present?
end
def oldest_active_date

View File

@ -4,6 +4,8 @@ module Gitlab
module Database
module Partitioning
class PartitionManager
UnsafeToDetachPartitionError = Class.new(StandardError)
def self.register(model)
raise ArgumentError, "Only models with a #partitioning_strategy can be registered." unless model.respond_to?(:partitioning_strategy)
@ -16,6 +18,7 @@ module Gitlab
LEASE_TIMEOUT = 1.minute
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
RETAIN_DETACHED_PARTITIONS_FOR = 1.week
attr_reader :models
@ -35,13 +38,16 @@ module Gitlab
partitions_to_create = missing_partitions(model)
create(partitions_to_create) unless partitions_to_create.empty?
if Feature.enabled?(:partition_pruning_dry_run)
if Feature.enabled?(:partition_pruning, default_enabled: :yaml)
partitions_to_detach = extra_partitions(model)
detach(partitions_to_detach) unless partitions_to_detach.empty?
end
end
rescue StandardError => e
Gitlab::AppLogger.error("Failed to create / detach partition(s) for #{model.table_name}: #{e.class}: #{e.message}")
Gitlab::AppLogger.error(message: "Failed to create / detach partition(s)",
table_name: model.table_name,
exception_class: e.class,
exception_message: e.message)
end
end
@ -54,7 +60,6 @@ module Gitlab
end
def extra_partitions(model)
return [] unless Feature.enabled?(:partition_pruning_dry_run)
return [] unless connection.table_exists?(model.table_name)
model.partitioning_strategy.extra_partitions
@ -74,7 +79,9 @@ module Gitlab
partitions.each do |partition|
connection.execute partition.to_sql
Gitlab::AppLogger.info("Created partition #{partition.partition_name} for table #{partition.table}")
Gitlab::AppLogger.info(message: "Created partition",
partition_name: partition.partition_name,
table_name: partition.table)
end
end
end
@ -89,7 +96,24 @@ module Gitlab
end
def detach_one_partition(partition)
Gitlab::AppLogger.info("Planning to detach #{partition.partition_name} for table #{partition.table}")
assert_partition_detachable!(partition)
connection.execute partition.to_detach_sql
Postgresql::DetachedPartition.create!(table_name: partition.partition_name,
drop_after: RETAIN_DETACHED_PARTITIONS_FOR.from_now)
Gitlab::AppLogger.info(message: "Detached Partition",
partition_name: partition.partition_name,
table_name: partition.table)
end
def assert_partition_detachable!(partition)
parent_table_identifier = "#{connection.current_schema}.#{partition.table}"
if (example_fk = PostgresForeignKey.by_referenced_table_identifier(parent_table_identifier).first)
raise UnsafeToDetachPartitionError, "Cannot detach #{partition.partition_name}, it would block while checking foreign key #{example_fk.name} on #{example_fk.constrained_table_identifier}"
end
end
def with_lock_retries(&block)

View File

@ -16,6 +16,7 @@ module Gitlab
gauge_present.set({ table: model.table_name }, strategy.current_partitions.size)
gauge_missing.set({ table: model.table_name }, strategy.missing_partitions.size)
gauge_extra.set({ table: model.table_name }, strategy.extra_partitions.size)
end
end
@ -28,6 +29,10 @@ module Gitlab
def gauge_missing
@gauge_missing ||= Gitlab::Metrics.gauge(:db_partitions_missing, 'Number of database partitions currently expected, but not present')
end
def gauge_extra
@gauge_extra ||= Gitlab::Metrics.gauge(:db_partitions_extra, 'Number of database partitions currently attached to tables, but outside of their retention window and scheduled to be dropped')
end
end
end
end

View File

@ -47,6 +47,13 @@ module Gitlab
SQL
end
def to_detach_sql
<<~SQL
ALTER TABLE #{conn.quote_table_name(table)}
DETACH PARTITION #{fully_qualified_partition}
SQL
end
def ==(other)
table == other.table && partition_name == other.partition_name && from == other.from && to == other.to
end

View File

@ -7,10 +7,14 @@ module Gitlab
belongs_to :postgres_partitioned_table, foreign_key: 'parent_identifier', primary_key: 'identifier'
scope :by_identifier, ->(identifier) do
scope :for_identifier, ->(identifier) do
raise ArgumentError, "Partition name is not fully qualified with a schema: #{identifier}" unless identifier =~ /^\w+\.\w+$/
find(identifier)
where(primary_key => identifier)
end
scope :by_identifier, ->(identifier) do
for_identifier(identifier).first!
end
scope :for_parent_table, ->(name) { where("parent_identifier = concat(current_schema(), '.', ?)", name).order(:name) }

View File

@ -170,7 +170,6 @@
category: testing
redis_slot: testing
aggregation: weekly
feature_flag: usage_data_i_testing_full_code_quality_report_total
- name: i_testing_web_performance_widget_total
category: testing
redis_slot: testing

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :organization, class: 'CustomerRelations::Organization' do
group
name { generate(:name) }
end
end

View File

@ -414,9 +414,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).to receive(:execute).with(/REFERENCES users \(id\)/)
@ -428,9 +428,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).to receive(:execute).with(/REFERENCES users \(id_convert_to_bigint\)/)
@ -446,9 +446,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
@ -463,9 +463,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).to receive(:execute).with(/ON DELETE CASCADE/)
@ -480,9 +480,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).not_to receive(:execute).with(/ON DELETE/)
@ -498,10 +498,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
end
@ -527,10 +527,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+foo/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo)
end
@ -557,10 +557,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+bar/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :bar)
end
@ -592,9 +592,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
expect(model).to receive(:execute).with('LOCK TABLE users, projects IN SHARE ROW EXCLUSIVE MODE')
expect(model).to receive(:execute).with(/REFERENCES users \(id\)/)
@ -614,9 +614,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).not_to receive(:concurrent_foreign_key_name)
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/ALTER TABLE projects VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
end
model.validate_foreign_key(:projects, :user_id, name: :foo)
@ -631,9 +631,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:concurrent_foreign_key_name)
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/ALTER TABLE projects VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
end
model.validate_foreign_key(:projects, :user_id)
@ -748,7 +748,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
after do
model.execute('RESET ALL')
model.execute('RESET statement_timeout')
end
it 'defines statement to 0 only for current transaction' do
@ -765,7 +765,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'when passing a blocks' do
it 'disables statement timeouts on session level and executes the block' do
expect(model).to receive(:execute).with('SET statement_timeout TO 0')
expect(model).to receive(:execute).with('RESET ALL').at_least(:once)
expect(model).to receive(:execute).with('RESET statement_timeout').at_least(:once)
expect { |block| model.disable_statement_timeout(&block) }.to yield_control
end
@ -777,7 +777,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
end
after do
model.execute('RESET ALL')
model.execute('RESET statement_timeout')
end
it 'defines statement to 0 for any code run inside the block' do
@ -804,12 +804,12 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
after do
# Use ActiveRecord::Base.connection instead of model.execute
# so that this call is not counted below
ActiveRecord::Base.connection.execute('RESET ALL')
ActiveRecord::Base.connection.execute('RESET statement_timeout')
end
it 'yields control without disabling the timeout or resetting' do
expect(model).not_to receive(:execute).with('SET statement_timeout TO 0')
expect(model).not_to receive(:execute).with('RESET ALL')
expect(model).not_to receive(:execute).with('RESET statement_timeout')
expect { |block| model.disable_statement_timeout(&block) }.to yield_control
end
@ -2532,7 +2532,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:execute).with(/ADD CONSTRAINT check_name_not_null/)
@ -2542,7 +2542,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
.and_return(true).exactly(1)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_check_constraint(
:test_table,
@ -2576,7 +2576,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:with_lock_retries).and_call_original
expect(model).to receive(:execute).with(/ADD CONSTRAINT check_name_not_null/)
@ -2585,7 +2585,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
.and_return(true).exactly(1)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_check_constraint(
:test_table,
@ -2618,9 +2618,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
expect(model).to receive(:check_constraint_exists?).and_return(true)
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(validate_sql)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.validate_check_constraint(:test_table, 'check_name')
end

View File

@ -0,0 +1,181 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do
include Database::TableSchemaHelpers
let(:connection) { ActiveRecord::Base.connection }
def expect_partition_present(name)
aggregate_failures do
expect(table_oid(name)).not_to be_nil
expect(Postgresql::DetachedPartition.find_by(table_name: name)).not_to be_nil
end
end
def expect_partition_removed(name)
aggregate_failures do
expect(table_oid(name)).to be_nil
expect(Postgresql::DetachedPartition.find_by(table_name: name)).to be_nil
end
end
before do
connection.execute(<<~SQL)
CREATE TABLE parent_table (
id bigserial not null,
created_at timestamptz not null,
primary key (id, created_at)
) PARTITION BY RANGE(created_at)
SQL
end
def create_partition(name:, table: 'parent_table', from:, to:, attached:, drop_after:)
from = from.beginning_of_month
to = to.beginning_of_month
full_name = "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{name}"
connection.execute(<<~SQL)
CREATE TABLE #{full_name}
PARTITION OF #{table}
FOR VALUES FROM ('#{from.strftime('%Y-%m-%d')}') TO ('#{to.strftime('%Y-%m-%d')}')
SQL
unless attached
connection.execute(<<~SQL)
ALTER TABLE #{table} DETACH PARTITION #{full_name}
SQL
end
Postgresql::DetachedPartition.create!(table_name: name,
drop_after: drop_after)
end
describe '#perform' do
context 'when the partition should not be dropped yet' do
it 'does not drop the partition' do
create_partition(name: 'test_partition',
from: 2.months.ago, to: 1.month.ago,
attached: false,
drop_after: 1.day.from_now)
subject.perform
expect_partition_present('test_partition')
end
end
context 'with a partition to drop' do
before do
create_partition(name: 'test_partition',
from: 2.months.ago,
to: 1.month.ago.beginning_of_month,
attached: false,
drop_after: 1.second.ago)
end
it 'drops the partition' do
subject.perform
expect(table_oid('test_partition')).to be_nil
end
context 'when the drop_detached_partitions feature flag is disabled' do
before do
stub_feature_flags(drop_detached_partitions: false)
end
it 'does not drop the partition' do
subject.perform
expect(table_oid('test_partition')).not_to be_nil
end
end
context 'when another process drops the table while the first waits for a lock' do
it 'skips the table' do
# Rspec's receive_method_chain does not support .and_wrap_original, so we need to nest here.
expect(Postgresql::DetachedPartition).to receive(:lock).and_wrap_original do |lock_meth|
locked = lock_meth.call
expect(locked).to receive(:find_by).and_wrap_original do |find_meth, *find_args|
# Another process drops the table then deletes this entry
Postgresql::DetachedPartition.where(*find_args).delete_all
find_meth.call(*find_args)
end
locked
end
expect(subject).not_to receive(:drop_one)
subject.perform
end
end
end
context 'when the partition to drop is still attached to its table' do
before do
create_partition(name: 'test_partition',
from: 2.months.ago,
to: 1.month.ago.beginning_of_month,
attached: true,
drop_after: 1.second.ago)
end
it 'does not drop the partition, but does remove the DetachedPartition entry' do
subject.perform
aggregate_failures do
expect(table_oid('test_partition')).not_to be_nil
expect(Postgresql::DetachedPartition.find_by(table_name: 'test_partition')).to be_nil
end
end
it 'removes the detached_partition entry' do
detached_partition = Postgresql::DetachedPartition.find_by!(table_name: 'test_partition')
subject.perform
expect(Postgresql::DetachedPartition.exists?(id: detached_partition.id)).to be_falsey
end
end
context 'with multiple partitions to drop' do
before do
create_partition(name: 'partition_1',
from: 3.months.ago,
to: 2.months.ago,
attached: false,
drop_after: 1.second.ago)
create_partition(name: 'partition_2',
from: 2.months.ago,
to: 1.month.ago,
attached: false,
drop_after: 1.second.ago)
end
it 'drops both partitions' do
subject.perform
expect_partition_removed('partition_1')
expect_partition_removed('partition_2')
end
context 'when the first drop returns an error' do
it 'still drops the second partition' do
expect(subject).to receive(:drop_one).ordered.and_raise('injected error')
expect(subject).to receive(:drop_one).ordered.and_call_original
subject.perform
# We don't know which partition we tried to drop first, so the tests here have to work with either one
expect(Postgresql::DetachedPartition.count).to eq(1)
errored_partition_name = Postgresql::DetachedPartition.first!.table_name
dropped_partition_name = (%w[partition_1 partition_2] - [errored_partition_name]).first
expect_partition_present(errored_partition_name)
expect_partition_removed(dropped_partition_name)
end
end
end
end
end

View File

@ -237,16 +237,6 @@ RSpec.describe Gitlab::Database::Partitioning::MonthlyStrategy do
expect(subject).to contain_exactly(min_value_to_may)
end
context 'when the feature flag is toggled off' do
before do
stub_feature_flags(partition_pruning_dry_run: false)
end
it 'is empty' do
expect(subject).to eq([])
end
end
end
context 'with a time retention policy of 2 months' do
@ -258,16 +248,6 @@ RSpec.describe Gitlab::Database::Partitioning::MonthlyStrategy do
Gitlab::Database::Partitioning::TimePartition.new(model.table_name, '2020-05-01', '2020-06-01', partition_name: 'partitioned_test_202005')
)
end
context 'when the feature flag is toggled off' do
before do
stub_feature_flags(partition_pruning_dry_run: false)
end
it 'is empty' do
expect(subject).to eq([])
end
end
end
end
end

View File

@ -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

View File

@ -8,7 +8,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionMonitoring do
let(:models) { [model] }
let(:model) { double(partitioning_strategy: partitioning_strategy, table_name: table) }
let(:partitioning_strategy) { double(missing_partitions: missing_partitions, current_partitions: current_partitions) }
let(:partitioning_strategy) { double(missing_partitions: missing_partitions, current_partitions: current_partitions, extra_partitions: extra_partitions) }
let(:table) { "some_table" }
let(:missing_partitions) do
@ -19,6 +19,10 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionMonitoring do
[double, double]
end
let(:extra_partitions) do
[double, double, double]
end
it 'reports number of present partitions' do
subject
@ -30,5 +34,11 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionMonitoring do
expect(Gitlab::Metrics.registry.get(:db_partitions_missing).get({ table: table })).to eq(missing_partitions.size)
end
it 'reports number of extra partitions' do
subject
expect(Gitlab::Metrics.registry.get(:db_partitions_extra).get({ table: table })).to eq(extra_partitions.size)
end
end
end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe CustomerRelations::Organization, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:group).with_foreign_key('group_id') }
end
describe 'validations' do
subject { create(:organization) }
it { is_expected.to validate_presence_of(:group) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).case_insensitive.scoped_to([:group_id]) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_length_of(:description).is_at_most(1024) }
end
describe '#name' do
it 'strips name' do
organization = described_class.new(name: ' GitLab ')
organization.valid?
expect(organization.name).to eq('GitLab')
end
end
describe '#find_by_name' do
let!(:group) { create(:group) }
let!(:organiztion1) { create(:organization, group: group, name: 'Test') }
let!(:organiztion2) { create(:organization, group: create(:group), name: 'Test') }
it 'strips name' do
expect(described_class.find_by_name(group.id, 'TEST')).to eq([organiztion1])
end
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Postgresql::DetachedPartition do
describe '#ready_to_drop' do
let_it_be(:drop_before) { Postgresql::DetachedPartition.create!(drop_after: 1.day.ago, table_name: 'old_table') }
let_it_be(:drop_after) { Postgresql::DetachedPartition.create!(drop_after: 1.day.from_now, table_name: 'new_table') }
it 'includes partitions that should be dropped before now' do
expect(Postgresql::DetachedPartition.ready_to_drop.to_a).to include(drop_before)
end
it 'does not include partitions that should be dropped after now' do
expect(Postgresql::DetachedPartition.ready_to_drop.to_a).not_to include(drop_after)
end
end
end

View File

@ -14,10 +14,10 @@ RSpec.shared_examples 'performs validation' do |validation_option|
it 'performs validation' do
expect(model).to receive(:disable_statement_timeout).and_call_original
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
expect(model).to receive(:execute).with(/statement_timeout/)
expect(model).to receive(:execute).with(/SET statement_timeout TO/)
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
expect(model).to receive(:execute).ordered.with(/RESET statement_timeout/)
model.add_concurrent_foreign_key(*args, **options.merge(validation_option))
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Database::DropDetachedPartitionsWorker do
describe '#perform' do
subject { described_class.new.perform }
let(:dropper) { instance_double('DropDetachedPartitions', perform: nil) }
let(:monitoring) { instance_double('PartitionMonitoring', report_metrics: nil) }
before do
allow(Gitlab::Database::Partitioning::DetachedPartitionDropper).to receive(:new).and_return(dropper)
allow(Gitlab::Database::Partitioning::PartitionMonitoring).to receive(:new).and_return(monitoring)
end
it 'delegates to DropPartitionsPendingDrop' do
expect(dropper).to receive(:perform)
subject
end
it 'reports partition metrics' do
expect(monitoring).to receive(:report_metrics)
subject
end
end
end