Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
844eb8879a
commit
1f3baf00bf
|
@ -46,7 +46,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
containerRepository: {},
|
||||
fetchTagsCount: false,
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
|
|
|
@ -9,6 +9,7 @@ query getContainerRepositoryTags(
|
|||
) {
|
||||
containerRepository(id: $id) {
|
||||
id
|
||||
tagsCount
|
||||
tags(after: $after, before: $before, first: $first, last: $last) {
|
||||
nodes {
|
||||
digest
|
||||
|
|
|
@ -25,9 +25,11 @@ import {
|
|||
UNFINISHED_STATUS,
|
||||
MISSING_OR_DELETED_IMAGE_BREADCRUMB,
|
||||
ROOT_IMAGE_TEXT,
|
||||
GRAPHQL_PAGE_SIZE,
|
||||
} from '../constants/index';
|
||||
import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql';
|
||||
import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
|
||||
import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_repository_tags.query.graphql';
|
||||
|
||||
export default {
|
||||
name: 'RegistryDetailsPage',
|
||||
|
@ -133,8 +135,8 @@ export default {
|
|||
awaitRefetchQueries: true,
|
||||
refetchQueries: [
|
||||
{
|
||||
query: getContainerRepositoryDetailsQuery,
|
||||
variables: this.queryVariables,
|
||||
query: getContainerRepositoryTagsQuery,
|
||||
variables: { ...this.queryVariables, first: GRAPHQL_PAGE_SIZE },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ export default {
|
|||
<gl-button
|
||||
:loading="loading"
|
||||
:variant="user.attention_requested ? 'warning' : 'default'"
|
||||
:icon="user.attention_requested ? 'star' : 'star-o'"
|
||||
:icon="user.attention_requested ? 'attention-solid' : 'attention'"
|
||||
:aria-label="tooltipTitle"
|
||||
size="small"
|
||||
category="tertiary"
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338350
|
|||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::memory
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -9,6 +9,8 @@ Rails.application.configure do
|
|||
config.action_cable.worker_pool_size = Gitlab::ActionCable::Config.worker_pool_size
|
||||
end
|
||||
|
||||
ActionCable::SubscriptionAdapter::Base.prepend(Gitlab::Patch::ActionCableSubscriptionAdapterIdentifier)
|
||||
|
||||
# https://github.com/rails/rails/blob/bb5ac1623e8de08c1b7b62b1368758f0d3bb6379/actioncable/lib/action_cable/subscription_adapter/redis.rb#L18
|
||||
ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda do |config|
|
||||
args = config.except(:adapter, :channel_prefix)
|
||||
|
|
|
@ -1354,7 +1354,7 @@ PUT /projects/:id
|
|||
```
|
||||
|
||||
For example, to toggle the setting for
|
||||
[shared runners on a GitLab.com project](../user/gitlab_com/index.md#shared-runner-cloud-runners):
|
||||
[shared runners on a GitLab.com project](../ci/runners/index.md):
|
||||
|
||||
```shell
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your-token>" \
|
||||
|
|
|
@ -1623,6 +1623,7 @@ Returns:
|
|||
- `201 Created` on success.
|
||||
- `404 User Not Found` if user cannot be found.
|
||||
- `403 Forbidden` if the user cannot be approved because they are blocked by an administrator or by LDAP synchronization.
|
||||
- `409 Conflict` if the user has been deactivated.
|
||||
|
||||
Example Responses:
|
||||
|
||||
|
|
|
@ -259,5 +259,5 @@ For very active repositories with a large number of references and files, you ca
|
|||
must be configured per-repository. The pack-objects cache also automatically works for forks. On GitLab.com, where the pack-objects cache is
|
||||
enabled on all Gitaly servers, we found that we no longer need a pre-clone step for `gitlab-org/gitlab` development.
|
||||
- Optimize your CI/CD jobs by seeding repository data in a pre-clone step with the
|
||||
[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) of GitLab Runner. See the
|
||||
[Runner Cloud for Linux](../runners/runner_cloud/linux_runner_cloud.md#pre-clone-script) for more details.
|
||||
[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) of GitLab Runner. See
|
||||
[SaaS runners on Linux](../runners/runner_cloud/linux_runner_cloud.md#pre-clone-script) for details.
|
||||
|
|
|
@ -5,12 +5,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
type: reference
|
||||
---
|
||||
|
||||
# GitLab Runner Cloud **(FREE)**
|
||||
# Runner SaaS **(FREE SAAS)**
|
||||
|
||||
If you are using self-managed GitLab or you want to use your own runners on GitLab.com, you can
|
||||
If you are using self-managed GitLab or you use GitLab.com but want to use your own runners, you can
|
||||
[install and configure your own runners](https://docs.gitlab.com/runner/install/).
|
||||
|
||||
If you are using GitLab SaaS (GitLab.com), your CI jobs automatically run on runners in the GitLab Runner Cloud.
|
||||
If you are using GitLab SaaS (GitLab.com), your CI jobs automatically run on runners provided by GitLab.
|
||||
No configuration is required. Your jobs can run on:
|
||||
|
||||
- [Linux runners](build_cloud/linux_build_cloud.md).
|
||||
|
|
|
@ -4,9 +4,9 @@ group: Runner
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Runner Cloud for Linux **(FREE)**
|
||||
# SaaS runners on Linux **(FREE SAAS)**
|
||||
|
||||
Runner Cloud runners for Linux run in autoscale mode and are powered by Google Cloud Platform.
|
||||
SaaS runners on Linux are autoscaled ephemeral Google Cloud Platform virtual machines.
|
||||
|
||||
Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each job, thus maximizing security. These shared runners are available on GitLab.com.
|
||||
|
||||
|
@ -38,7 +38,7 @@ These runners share a [distributed cache](https://docs.gitlab.com/runner/configu
|
|||
|
||||
## Pre-clone script
|
||||
|
||||
Cloud runners for Linux provide a way to run commands in a CI
|
||||
With SaaS runners on Linux, you can run commands in a CI
|
||||
job before the runner attempts to run `git init` and `git fetch` to
|
||||
download a GitLab repository. The
|
||||
[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
|
||||
|
|
|
@ -4,9 +4,9 @@ group: Runner
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# VM instances and images for Runner Cloud for macOS **(FREE)**
|
||||
# VM instances and images for SaaS runners on macOS **(FREE SAAS)**
|
||||
|
||||
When you use the Runner Cloud for macOS:
|
||||
When you use SaaS runners on macOS:
|
||||
|
||||
- Each of your jobs runs in a newly provisioned VM, which is dedicated to the specific job.
|
||||
- The VM is active only for the duration of the job and immediately deleted.
|
||||
|
|
|
@ -4,19 +4,20 @@ group: Runner
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Runner Cloud for macOS (Beta) **(FREE SAAS)**
|
||||
# SaaS runners on macOS (Beta) **(FREE SAAS)**
|
||||
|
||||
The Runner Cloud for macOS Beta provides on-demand runners integrated with GitLab SaaS [CI/CD](../../../ci/index.md).
|
||||
SaaS runners on macOS provide an on-demand macOS build environment integrated with
|
||||
GitLab SaaS [CI/CD](../../../ci/index.md).
|
||||
Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS, iOS, tvOS). You can take advantage
|
||||
of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a
|
||||
build environment.
|
||||
|
||||
Cloud runners for macOS are in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
|
||||
SaaS runners on macOS are in [Beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
|
||||
and shouldn't be relied upon for mission-critical production jobs.
|
||||
|
||||
## Quickstart
|
||||
|
||||
To start using Runner Cloud for macOS Beta, you must submit an access request [issue](https://gitlab.com/gitlab-com/macos-buildcloud-runners-beta/-/issues/new?issuable_template=beta_access_request). After your
|
||||
To start using SaaS runners on macOS, you must submit an access request [issue](https://gitlab.com/gitlab-com/macos-buildcloud-runners-beta/-/issues/new?issuable_template=beta_access_request). After your
|
||||
access has been granted and your build environment configured, you must configure your
|
||||
`.gitlab-ci.yml` pipeline file:
|
||||
|
||||
|
@ -28,10 +29,10 @@ The runners automatically run your build.
|
|||
|
||||
## Example `.gitlab-ci.yml` file
|
||||
|
||||
The following sample `.gitlab-ci.yml` file shows how to start using the runners for macOS:
|
||||
The following sample `.gitlab-ci.yml` file shows how to start using the SaaS runners on macOS:
|
||||
|
||||
```yaml
|
||||
.macos_buildcloud_runners:
|
||||
.macos_saas_runners:
|
||||
tags:
|
||||
- shared-macos-amd64
|
||||
image: macos-11-xcode-12
|
||||
|
@ -45,14 +46,14 @@ before_script:
|
|||
|
||||
build:
|
||||
extends:
|
||||
- .macos_buildcloud_runners
|
||||
- .macos_saas_runners
|
||||
stage: build
|
||||
script:
|
||||
- echo "running scripts in the build job"
|
||||
|
||||
test:
|
||||
extends:
|
||||
- .macos_buildcloud_runners
|
||||
- .macos_saas_runners
|
||||
stage: test
|
||||
script:
|
||||
- echo "running scripts in the test job"
|
||||
|
|
|
@ -4,9 +4,9 @@ group: Runner
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Runner Cloud for Windows (beta) **(FREE)**
|
||||
# SaaS runners on Windows (beta) **(FREE SAAS)**
|
||||
|
||||
Runner Cloud runners for Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
|
||||
SaaS runners on Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
|
||||
and shouldn't be used for production workloads.
|
||||
|
||||
During this beta period, the [shared runner pipeline quota](../../../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota)
|
||||
|
|
|
@ -139,8 +139,9 @@ In these cases, use the following workflow:
|
|||
and approval from the VP of Development, the DRI for Development Guidelines,
|
||||
@clefelhocz1.
|
||||
|
||||
1. After all approvals are complete, assign the merge request to the
|
||||
Technical Writer for [Development Guidelines](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines)
|
||||
1. After all approvals are complete, review the page's metadata to
|
||||
[find a Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments)
|
||||
who can help you merge the changes.
|
||||
for final content review and merge. The Technical Writer may ask for
|
||||
additional approvals as previously suggested before merging the MR.
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ end
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69372) in GitLab 14.3.
|
||||
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/338350) in GitLab 14.4.
|
||||
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/338350) in GitLab 14.6.
|
||||
|
||||
The deduplication always take into account the latest binary replication pointer, not the first one.
|
||||
This happens because we drop the same job scheduled for the second time and the Write-Ahead Log (WAL) is lost.
|
||||
|
@ -353,15 +354,11 @@ This way we are always comparing the latest binary replication pointer,
|
|||
making sure that we read from the replica that is fully caught up.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available,
|
||||
ask an administrator to [enable the preserve_latest_wal_locations_for_idempotent_jobs flag](../administration/feature_flags.md).
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available.
|
||||
To make it available,
|
||||
ask an administrator to [enable the `preserve_latest_wal_locations_for_idempotent_jobs` flag](../administration/feature_flags.md).
|
||||
On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to
|
||||
[disable the feature flag](../administration/feature_flags.md) named preserve_latest_wal_locations_for_idempotent_jobs flag.
|
||||
|
||||
This feature flag is related to GitLab development and is not intended to be used by GitLab administrators, though.
|
||||
On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.
|
||||
On GitLab.com, this feature is available.
|
||||
|
||||
## Limited capacity worker
|
||||
|
||||
|
|
|
@ -302,7 +302,8 @@ The GitLab Runner server requirements depend on:
|
|||
|
||||
Since the nature of the jobs varies for each use case, you need to experiment by adjusting the job concurrency to get the optimum setting.
|
||||
|
||||
For reference, the GitLab.com Runner Cloud [auto-scaling runner for Linux](../ci/runners/build_cloud/linux_build_cloud.md) is configured so that a **single job** runs in a **single instance** with:
|
||||
For reference, the [SaaS runners on Linux](../ci/runners/build_cloud/linux_build_cloud.md)
|
||||
are configured so that a **single job** runs in a **single instance** with:
|
||||
|
||||
- 1 vCPU.
|
||||
- 3.75 GB of RAM.
|
||||
|
|
|
@ -42,5 +42,6 @@ Align your work across teams.
|
|||
- [Epics](../user/group/epics/index.md)
|
||||
- [Multi-level epics](../user/group/epics/manage_epics.md#multi-level-child-epics)
|
||||
- [Epic boards](../user/group/epics/epic_boards.md)
|
||||
- [View heath status](../user/project/issues/managing_issues.md#health-status)
|
||||
- [View health status](../user/project/issues/managing_issues.md#health-status)
|
||||
- [Roadmaps](../user/group/roadmap/index.md)
|
||||
- [Planning hierarchies](../user/group/planning_hierarchy/index.md)
|
||||
|
|
|
@ -200,11 +200,11 @@ The following limits apply for [Webhooks](../project/integrations/webhooks.md):
|
|||
| [Number of webhooks](../../administration/instance_limits.md#number-of-webhooks) | `100` per project, `50` per group | `100` per project, `50` per group |
|
||||
| Maximum payload size | 25 MB | 25 MB |
|
||||
|
||||
## Shared Runner Cloud runners
|
||||
## Runner SaaS
|
||||
|
||||
GitLab has shared runners on GitLab.com that you can use to run your CI jobs.
|
||||
Runner SaaS is the hosted, secure, and managed build environment you can use to run CI/CD jobs for your GitLab.com hosted project.
|
||||
|
||||
For more information, see [GitLab Runner Cloud runners](../../ci/runners/index.md).
|
||||
For more information, see [Runner SaaS](../../ci/runners/index.md).
|
||||
|
||||
## Sidekiq
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ graph TD
|
|||
Child_epic --> Issue2
|
||||
```
|
||||
|
||||
Also, read more about possible [planning hierarchies](../planning_hierarchy/index.md).
|
||||
|
||||
## Roadmap in epics **(ULTIMATE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7327) in GitLab 11.10.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
type: reference
|
||||
stage: Plan
|
||||
group: Product Planning
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Planning hierarchies **(PREMIUM)**
|
||||
|
||||
Planning hierarchies are an integral part of breaking down your work in GitLab.
|
||||
To understand how you can use epics and issues together in hierarchies, remember the following:
|
||||
|
||||
- [Epics](../epics/index.md) exist in groups.
|
||||
- [Issues](../../project/issues/index.md) exist in projects.
|
||||
|
||||
GitLab is not opinionated on how you structure your work and the hierarchy you can build with multi-level
|
||||
epics. For example, you can use the hierarchy as a folder of issues for bigger initiatives.
|
||||
|
||||
To learn about hierarchies in general, common frameworks, and using GitLab for
|
||||
portfolio management, see
|
||||
[How to use GitLab for Agile portfolio planning and project management](https://about.gitlab.com/blog/2020/11/11/gitlab-for-agile-portfolio-planning-project-management/).
|
||||
|
||||
## Hierarchies with epics
|
||||
|
||||
With epics, you can achieve the following hierarchy:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Group_epic --> Project1_Issue1
|
||||
Group_epic --> Project1_Issue2
|
||||
Group_epic --> Project2_Issue1
|
||||
```
|
||||
|
||||
### Hierarchies with multi-level epics **(ULTIMATE)**
|
||||
|
||||
With the addition of [multi-level epics](../epics/manage_epics.md#multi-level-child-epics) and up to
|
||||
seven levels of nested epics, you can achieve the following hierarchy:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
direction TD
|
||||
class Epic
|
||||
class Issue
|
||||
|
||||
Epic *-- "0..7" Epic
|
||||
Epic "1" *-- "0..*" Issue
|
||||
```
|
||||
|
||||
## View ancestry of an epic
|
||||
|
||||
In an epic, you can view the ancestors as parents in the right sidebar under **Ancestors**.
|
||||
|
||||
![epics state dropdown](img/epic-view-ancestors-in-sidebar_v14_6.png)
|
||||
|
||||
## View ancestry of an issue
|
||||
|
||||
In an issue, you can view the parented epic above the issue in the right sidebar under **Epic**.
|
||||
|
||||
![epics state dropdown](img/issue-view-parent-epic-in-sidebar_v14_6.png)
|
|
@ -4,6 +4,7 @@ module Gitlab
|
|||
module Database
|
||||
module MigrationHelpers
|
||||
include Migrations::BackgroundMigrationHelpers
|
||||
include Migrations::BatchedBackgroundMigrationHelpers
|
||||
include DynamicModelHelpers
|
||||
include RenameTableHelpers
|
||||
include AsyncIndexes::MigrationHelpers
|
||||
|
|
|
@ -5,11 +5,7 @@ module Gitlab
|
|||
module Migrations
|
||||
module BackgroundMigrationHelpers
|
||||
BATCH_SIZE = 1_000 # Number of rows to process per job
|
||||
SUB_BATCH_SIZE = 100 # Number of rows to process per sub-batch
|
||||
JOB_BUFFER_SIZE = 1_000 # Number of jobs to bulk queue at a time
|
||||
BATCH_CLASS_NAME = 'PrimaryKeyBatchingStrategy' # Default batch class for batched migrations
|
||||
BATCH_MIN_VALUE = 1 # Default minimum value for batched migrations
|
||||
BATCH_MIN_DELAY = 2.minutes.freeze # Minimum delay between batched migrations
|
||||
|
||||
# Bulk queues background migration jobs for an entire table, batched by ID range.
|
||||
# "Bulk" meaning many jobs will be pushed at a time for efficiency.
|
||||
|
@ -170,102 +166,6 @@ module Gitlab
|
|||
duration
|
||||
end
|
||||
|
||||
# Creates a batched background migration for the given table. A batched migration runs one job
|
||||
# at a time, computing the bounds of the next batch based on the current migration settings and the previous
|
||||
# batch bounds. Each job's execution status is tracked in the database as the migration runs. The given job
|
||||
# class must be present in the Gitlab::BackgroundMigration module, and the batch class (if specified) must be
|
||||
# present in the Gitlab::BackgroundMigration::BatchingStrategies module.
|
||||
#
|
||||
# If migration with same job_class_name, table_name, column_name, and job_aruments already exists, this helper
|
||||
# will log an warning and not create a new one.
|
||||
#
|
||||
# job_class_name - The background migration job class as a string
|
||||
# batch_table_name - The name of the table the migration will batch over
|
||||
# batch_column_name - The name of the column the migration will batch over
|
||||
# job_arguments - Extra arguments to pass to the job instance when the migration runs
|
||||
# job_interval - The pause interval between each job's execution, minimum of 2 minutes
|
||||
# batch_min_value - The value in the column the batching will begin at
|
||||
# batch_max_value - The value in the column the batching will end at, defaults to `SELECT MAX(batch_column)`
|
||||
# batch_class_name - The name of the class that will be called to find the range of each next batch
|
||||
# batch_size - The maximum number of rows per job
|
||||
# sub_batch_size - The maximum number of rows processed per "iteration" within the job
|
||||
#
|
||||
#
|
||||
# *Returns the created BatchedMigration record*
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# queue_batched_background_migration(
|
||||
# 'CopyColumnUsingBackgroundMigrationJob',
|
||||
# :events,
|
||||
# :id,
|
||||
# job_interval: 2.minutes,
|
||||
# other_job_arguments: ['column1', 'column2'])
|
||||
#
|
||||
# Where the the background migration exists:
|
||||
#
|
||||
# class Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob
|
||||
# def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, *other_args)
|
||||
# # do something
|
||||
# end
|
||||
# end
|
||||
def queue_batched_background_migration( # rubocop:disable Metrics/ParameterLists
|
||||
job_class_name,
|
||||
batch_table_name,
|
||||
batch_column_name,
|
||||
*job_arguments,
|
||||
job_interval:,
|
||||
batch_min_value: BATCH_MIN_VALUE,
|
||||
batch_max_value: nil,
|
||||
batch_class_name: BATCH_CLASS_NAME,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
|
||||
if Gitlab::Database::BackgroundMigration::BatchedMigration.for_configuration(job_class_name, batch_table_name, batch_column_name, job_arguments).exists?
|
||||
Gitlab::AppLogger.warn "Batched background migration not enqueued because it already exists: " \
|
||||
"job_class_name: #{job_class_name}, table_name: #{batch_table_name}, column_name: #{batch_column_name}, " \
|
||||
"job_arguments: #{job_arguments.inspect}"
|
||||
return
|
||||
end
|
||||
|
||||
job_interval = BATCH_MIN_DELAY if job_interval < BATCH_MIN_DELAY
|
||||
|
||||
batch_max_value ||= connection.select_value(<<~SQL)
|
||||
SELECT MAX(#{connection.quote_column_name(batch_column_name)})
|
||||
FROM #{connection.quote_table_name(batch_table_name)}
|
||||
SQL
|
||||
|
||||
migration_status = batch_max_value.nil? ? :finished : :active
|
||||
batch_max_value ||= batch_min_value
|
||||
|
||||
migration = Gitlab::Database::BackgroundMigration::BatchedMigration.create!(
|
||||
job_class_name: job_class_name,
|
||||
table_name: batch_table_name,
|
||||
column_name: batch_column_name,
|
||||
job_arguments: job_arguments,
|
||||
interval: job_interval,
|
||||
min_value: batch_min_value,
|
||||
max_value: batch_max_value,
|
||||
batch_class_name: batch_class_name,
|
||||
batch_size: batch_size,
|
||||
sub_batch_size: sub_batch_size,
|
||||
status: migration_status)
|
||||
|
||||
# This guard is necessary since #total_tuple_count was only introduced schema-wise,
|
||||
# after this migration helper had been used for the first time.
|
||||
return migration unless migration.respond_to?(:total_tuple_count)
|
||||
|
||||
# We keep track of the estimated number of tuples to reason later
|
||||
# about the overall progress of a migration.
|
||||
migration.total_tuple_count = Gitlab::Database::SharedModel.using_connection(connection) do
|
||||
Gitlab::Database::PgClass.for_table(batch_table_name)&.cardinality_estimate
|
||||
end
|
||||
migration.save!
|
||||
|
||||
migration
|
||||
end
|
||||
|
||||
# Force a background migration to complete.
|
||||
#
|
||||
# WARNING: This method will block the caller and move the background migration from an
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Database
|
||||
module Migrations
|
||||
# BatchedBackgroundMigrations are a new approach to scheduling and executing background migrations, which uses
|
||||
# persistent state in the database to track each migration. This avoids having to batch over an entire table and
|
||||
# schedule a large number of sidekiq jobs upfront. It also provides for more flexibility as the migration runs,
|
||||
# as it can be paused and restarted, and have configuration values like the batch size updated dynamically as the
|
||||
# migration runs.
|
||||
#
|
||||
# For now, these migrations are not considered ready for general use, for more information see the tracking epic:
|
||||
# https://gitlab.com/groups/gitlab-org/-/epics/6751
|
||||
module BatchedBackgroundMigrationHelpers
|
||||
BATCH_SIZE = 1_000 # Number of rows to process per job
|
||||
SUB_BATCH_SIZE = 100 # Number of rows to process per sub-batch
|
||||
BATCH_CLASS_NAME = 'PrimaryKeyBatchingStrategy' # Default batch class for batched migrations
|
||||
BATCH_MIN_VALUE = 1 # Default minimum value for batched migrations
|
||||
BATCH_MIN_DELAY = 2.minutes.freeze # Minimum delay between batched migrations
|
||||
|
||||
# Creates a batched background migration for the given table. A batched migration runs one job
|
||||
# at a time, computing the bounds of the next batch based on the current migration settings and the previous
|
||||
# batch bounds. Each job's execution status is tracked in the database as the migration runs. The given job
|
||||
# class must be present in the Gitlab::BackgroundMigration module, and the batch class (if specified) must be
|
||||
# present in the Gitlab::BackgroundMigration::BatchingStrategies module.
|
||||
#
|
||||
# If migration with same job_class_name, table_name, column_name, and job_aruments already exists, this helper
|
||||
# will log an warning and not create a new one.
|
||||
#
|
||||
# job_class_name - The background migration job class as a string
|
||||
# batch_table_name - The name of the table the migration will batch over
|
||||
# batch_column_name - The name of the column the migration will batch over
|
||||
# job_arguments - Extra arguments to pass to the job instance when the migration runs
|
||||
# job_interval - The pause interval between each job's execution, minimum of 2 minutes
|
||||
# batch_min_value - The value in the column the batching will begin at
|
||||
# batch_max_value - The value in the column the batching will end at, defaults to `SELECT MAX(batch_column)`
|
||||
# batch_class_name - The name of the class that will be called to find the range of each next batch
|
||||
# batch_size - The maximum number of rows per job
|
||||
# sub_batch_size - The maximum number of rows processed per "iteration" within the job
|
||||
#
|
||||
# *Returns the created BatchedMigration record*
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# queue_batched_background_migration(
|
||||
# 'CopyColumnUsingBackgroundMigrationJob',
|
||||
# :events,
|
||||
# :id,
|
||||
# job_interval: 2.minutes,
|
||||
# other_job_arguments: ['column1', 'column2'])
|
||||
#
|
||||
# Where the the background migration exists:
|
||||
#
|
||||
# class Gitlab::BackgroundMigration::CopyColumnUsingBackgroundMigrationJob
|
||||
# def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, *other_args)
|
||||
# # do something
|
||||
# end
|
||||
# end
|
||||
def queue_batched_background_migration( # rubocop:disable Metrics/ParameterLists
|
||||
job_class_name,
|
||||
batch_table_name,
|
||||
batch_column_name,
|
||||
*job_arguments,
|
||||
job_interval:,
|
||||
batch_min_value: BATCH_MIN_VALUE,
|
||||
batch_max_value: nil,
|
||||
batch_class_name: BATCH_CLASS_NAME,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
|
||||
if Gitlab::Database::BackgroundMigration::BatchedMigration.for_configuration(job_class_name, batch_table_name, batch_column_name, job_arguments).exists?
|
||||
Gitlab::AppLogger.warn "Batched background migration not enqueued because it already exists: " \
|
||||
"job_class_name: #{job_class_name}, table_name: #{batch_table_name}, column_name: #{batch_column_name}, " \
|
||||
"job_arguments: #{job_arguments.inspect}"
|
||||
return
|
||||
end
|
||||
|
||||
job_interval = BATCH_MIN_DELAY if job_interval < BATCH_MIN_DELAY
|
||||
|
||||
batch_max_value ||= connection.select_value(<<~SQL)
|
||||
SELECT MAX(#{connection.quote_column_name(batch_column_name)})
|
||||
FROM #{connection.quote_table_name(batch_table_name)}
|
||||
SQL
|
||||
|
||||
migration_status = batch_max_value.nil? ? :finished : :active
|
||||
batch_max_value ||= batch_min_value
|
||||
|
||||
migration = Gitlab::Database::BackgroundMigration::BatchedMigration.create!(
|
||||
job_class_name: job_class_name,
|
||||
table_name: batch_table_name,
|
||||
column_name: batch_column_name,
|
||||
job_arguments: job_arguments,
|
||||
interval: job_interval,
|
||||
min_value: batch_min_value,
|
||||
max_value: batch_max_value,
|
||||
batch_class_name: batch_class_name,
|
||||
batch_size: batch_size,
|
||||
sub_batch_size: sub_batch_size,
|
||||
status: migration_status)
|
||||
|
||||
# This guard is necessary since #total_tuple_count was only introduced schema-wise,
|
||||
# after this migration helper had been used for the first time.
|
||||
return migration unless migration.respond_to?(:total_tuple_count)
|
||||
|
||||
# We keep track of the estimated number of tuples to reason later
|
||||
# about the overall progress of a migration.
|
||||
migration.total_tuple_count = Gitlab::Database::SharedModel.using_connection(connection) do
|
||||
Gitlab::Database::PgClass.for_table(batch_table_name)&.cardinality_estimate
|
||||
end
|
||||
migration.save!
|
||||
|
||||
migration
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Modifies https://github.com/rails/rails/blob/v6.1.4.1/actioncable/lib/action_cable/subscription_adapter/base.rb so
|
||||
# that we do not overwrite an id that was explicitly set to `nil` in cable.yml.
|
||||
# This is needed to support GCP Memorystore. See https://github.com/rails/rails/issues/38244.
|
||||
|
||||
module Gitlab
|
||||
module Patch
|
||||
module ActionCableSubscriptionAdapterIdentifier
|
||||
def identifier
|
||||
@server.config.cable.has_key?(:id) ? @server.config.cable[:id] : super # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -167,6 +167,7 @@ export const imageTagsMock = (nodes = tagsMock) => ({
|
|||
data: {
|
||||
containerRepository: {
|
||||
id: containerRepositoryMock.id,
|
||||
tagsCount: nodes.length,
|
||||
tags: {
|
||||
nodes,
|
||||
pageInfo: { ...tagsPageInfo },
|
||||
|
@ -191,7 +192,7 @@ export const graphQLImageDetailsMock = (override) => ({
|
|||
data: {
|
||||
containerRepository: {
|
||||
...containerRepositoryMock,
|
||||
|
||||
tagsCount: tagsMock.length,
|
||||
tags: {
|
||||
nodes: tagsMock,
|
||||
pageInfo: { ...tagsPageInfo },
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { GlKeysetPagination } from '@gitlab/ui';
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { nextTick } from 'vue';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -22,6 +23,7 @@ import {
|
|||
} from '~/packages_and_registries/container_registry/explorer/constants';
|
||||
import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql';
|
||||
import getContainerRepositoryDetailsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_details.query.graphql';
|
||||
import getContainerRepositoryTagsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql';
|
||||
|
||||
import component from '~/packages_and_registries/container_registry/explorer/pages/details.vue';
|
||||
import Tracking from '~/tracking';
|
||||
|
@ -32,6 +34,7 @@ import {
|
|||
containerRepositoryMock,
|
||||
graphQLEmptyImageDetailsMock,
|
||||
tagsMock,
|
||||
imageTagsMock,
|
||||
} from '../mock_data';
|
||||
import { DeleteModal } from '../stubs';
|
||||
|
||||
|
@ -67,12 +70,13 @@ describe('Details Page', () => {
|
|||
|
||||
const waitForApolloRequestRender = async () => {
|
||||
await waitForPromises();
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
};
|
||||
|
||||
const mountComponent = ({
|
||||
resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()),
|
||||
mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock),
|
||||
tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock)),
|
||||
options,
|
||||
config = {},
|
||||
} = {}) => {
|
||||
|
@ -81,6 +85,7 @@ describe('Details Page', () => {
|
|||
const requestHandlers = [
|
||||
[getContainerRepositoryDetailsQuery, resolver],
|
||||
[deleteContainerRepositoryTagsMutation, mutationResolver],
|
||||
[getContainerRepositoryTagsQuery, tagsResolver],
|
||||
];
|
||||
|
||||
apolloProvider = createMockApollo(requestHandlers);
|
||||
|
@ -242,38 +247,49 @@ describe('Details Page', () => {
|
|||
|
||||
describe('confirmDelete event', () => {
|
||||
let mutationResolver;
|
||||
let tagsResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock);
|
||||
mountComponent({ mutationResolver });
|
||||
tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock));
|
||||
mountComponent({ mutationResolver, tagsResolver });
|
||||
|
||||
return waitForApolloRequestRender();
|
||||
});
|
||||
|
||||
describe('when one item is selected to be deleted', () => {
|
||||
it('calls apollo mutation with the right parameters', async () => {
|
||||
it('calls apollo mutation with the right parameters and refetches the tags list query', async () => {
|
||||
findTagsList().vm.$emit('delete', [cleanTags[0]]);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
findDeleteModal().vm.$emit('confirmDelete');
|
||||
|
||||
expect(mutationResolver).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ tagNames: [cleanTags[0].name] }),
|
||||
);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(tagsResolver).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when more than one item is selected to be deleted', () => {
|
||||
it('calls apollo mutation with the right parameters', async () => {
|
||||
it('calls apollo mutation with the right parameters and refetches the tags list query', async () => {
|
||||
findTagsList().vm.$emit('delete', tagsMock);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
findDeleteModal().vm.$emit('confirmDelete');
|
||||
|
||||
expect(mutationResolver).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }),
|
||||
);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(tagsResolver).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -382,7 +398,7 @@ describe('Details Page', () => {
|
|||
|
||||
findPartialCleanupAlert().vm.$emit('dismiss');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
expect(axios.post).toHaveBeenCalledWith(config.userCalloutsPath, {
|
||||
feature_name: config.userCalloutId,
|
||||
|
@ -472,7 +488,7 @@ describe('Details Page', () => {
|
|||
await waitForApolloRequestRender();
|
||||
findDetailsHeader().vm.$emit('delete');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
};
|
||||
|
||||
it('on delete event it deletes the image', async () => {
|
||||
|
@ -497,13 +513,13 @@ describe('Details Page', () => {
|
|||
|
||||
findDeleteImage().vm.$emit('start');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
expect(findTagsLoader().exists()).toBe(true);
|
||||
|
||||
findDeleteImage().vm.$emit('end');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
expect(findTagsLoader().exists()).toBe(false);
|
||||
});
|
||||
|
@ -513,7 +529,7 @@ describe('Details Page', () => {
|
|||
|
||||
findDeleteImage().vm.$emit('error');
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
await nextTick();
|
||||
|
||||
expect(findDeleteAlert().props('deleteAlertType')).toBe(ALERT_DANGER_IMAGE);
|
||||
});
|
||||
|
|
|
@ -23,8 +23,8 @@ describe('Attention require toggle', () => {
|
|||
|
||||
it.each`
|
||||
attentionRequested | icon
|
||||
${true} | ${'star'}
|
||||
${false} | ${'star-o'}
|
||||
${true} | ${'attention-solid'}
|
||||
${false} | ${'attention'}
|
||||
`(
|
||||
'renders $icon icon when attention_requested is $attentionRequested',
|
||||
({ attentionRequested, icon }) => {
|
|
@ -0,0 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'ActionCableSubscriptionAdapterIdentifier override' do
|
||||
describe '#identifier' do
|
||||
context 'when id key is nil on cable.yml' do
|
||||
it 'does not override server config id with action cable pid' do
|
||||
config = {
|
||||
adapter: 'redis',
|
||||
url: 'unix:/home/localuser/redis/redis.socket',
|
||||
channel_prefix: 'test_',
|
||||
id: nil
|
||||
}
|
||||
::ActionCable::Server::Base.config.cable = config
|
||||
|
||||
sub = ActionCable.server.pubsub.send(:redis_connection)
|
||||
|
||||
expect(sub.connection[:id]).to eq('redis:///home/localuser/redis/redis.socket/0')
|
||||
expect(ActionCable.server.config.cable[:id]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -354,161 +354,6 @@ RSpec.describe Gitlab::Database::Migrations::BackgroundMigrationHelpers do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#queue_batched_background_migration' do
|
||||
let(:pgclass_info) { instance_double('Gitlab::Database::PgClass', cardinality_estimate: 42) }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Database::PgClass).to receive(:for_table).and_call_original
|
||||
end
|
||||
|
||||
context 'when such migration already exists' do
|
||||
it 'does not create duplicate migration' do
|
||||
create(
|
||||
:batched_background_migration,
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: :projects,
|
||||
column_name: :id,
|
||||
interval: 10.minutes,
|
||||
min_value: 5,
|
||||
max_value: 1005,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 200,
|
||||
sub_batch_size: 20,
|
||||
job_arguments: [[:id], [:id_convert_to_bigint]]
|
||||
)
|
||||
|
||||
expect do
|
||||
model.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
[:id], [:id_convert_to_bigint],
|
||||
job_interval: 5.minutes,
|
||||
batch_min_value: 5,
|
||||
batch_max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10)
|
||||
end.not_to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates the database record for the migration' do
|
||||
expect(Gitlab::Database::PgClass).to receive(:for_table).with(:projects).and_return(pgclass_info)
|
||||
|
||||
expect do
|
||||
model.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
job_interval: 5.minutes,
|
||||
batch_min_value: 5,
|
||||
batch_max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: 'projects',
|
||||
column_name: 'id',
|
||||
interval: 300,
|
||||
min_value: 5,
|
||||
max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10,
|
||||
job_arguments: %w[],
|
||||
status: 'active',
|
||||
total_tuple_count: pgclass_info.cardinality_estimate)
|
||||
end
|
||||
|
||||
context 'when the job interval is lower than the minimum' do
|
||||
let(:minimum_delay) { described_class::BATCH_MIN_DELAY }
|
||||
|
||||
it 'sets the job interval to the minimum value' do
|
||||
expect do
|
||||
model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: minimum_delay - 1.minute)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.interval).to eq(minimum_delay)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when additional arguments are passed to the method' do
|
||||
it 'saves the arguments on the database record' do
|
||||
expect do
|
||||
model.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
'my',
|
||||
'arguments',
|
||||
job_interval: 5.minutes,
|
||||
batch_max_value: 1000)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: 'projects',
|
||||
column_name: 'id',
|
||||
interval: 300,
|
||||
min_value: 1,
|
||||
max_value: 1000,
|
||||
job_arguments: %w[my arguments])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the max_value is not given' do
|
||||
context 'when records exist in the database' do
|
||||
let!(:event1) { create(:event) }
|
||||
let!(:event2) { create(:event) }
|
||||
let!(:event3) { create(:event) }
|
||||
|
||||
it 'creates the record with the current max value' do
|
||||
expect do
|
||||
model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.max_value).to eq(event3.id)
|
||||
end
|
||||
|
||||
it 'creates the record with an active status' do
|
||||
expect do
|
||||
model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_active
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the database is empty' do
|
||||
it 'sets the max value to the min value' do
|
||||
expect do
|
||||
model.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.max_value).to eq(created_migration.min_value)
|
||||
end
|
||||
|
||||
it 'creates the record with a finished status' do
|
||||
expect do
|
||||
model.queue_batched_background_migration('MyJobClass', :projects, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_finished
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#migrate_async' do
|
||||
it 'calls BackgroundMigrationWorker.perform_async' do
|
||||
expect(BackgroundMigrationWorker).to receive(:perform_async).with("Class", "hello", "world")
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers do
|
||||
let(:migration) do
|
||||
ActiveRecord::Migration.new.extend(described_class)
|
||||
end
|
||||
|
||||
describe '#queue_batched_background_migration' do
|
||||
let(:pgclass_info) { instance_double('Gitlab::Database::PgClass', cardinality_estimate: 42) }
|
||||
|
||||
before do
|
||||
allow(Gitlab::Database::PgClass).to receive(:for_table).and_call_original
|
||||
end
|
||||
|
||||
context 'when such migration already exists' do
|
||||
it 'does not create duplicate migration' do
|
||||
create(
|
||||
:batched_background_migration,
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: :projects,
|
||||
column_name: :id,
|
||||
interval: 10.minutes,
|
||||
min_value: 5,
|
||||
max_value: 1005,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 200,
|
||||
sub_batch_size: 20,
|
||||
job_arguments: [[:id], [:id_convert_to_bigint]]
|
||||
)
|
||||
|
||||
expect do
|
||||
migration.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
[:id], [:id_convert_to_bigint],
|
||||
job_interval: 5.minutes,
|
||||
batch_min_value: 5,
|
||||
batch_max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10)
|
||||
end.not_to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates the database record for the migration' do
|
||||
expect(Gitlab::Database::PgClass).to receive(:for_table).with(:projects).and_return(pgclass_info)
|
||||
|
||||
expect do
|
||||
migration.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
job_interval: 5.minutes,
|
||||
batch_min_value: 5,
|
||||
batch_max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: 'projects',
|
||||
column_name: 'id',
|
||||
interval: 300,
|
||||
min_value: 5,
|
||||
max_value: 1000,
|
||||
batch_class_name: 'MyBatchClass',
|
||||
batch_size: 100,
|
||||
sub_batch_size: 10,
|
||||
job_arguments: %w[],
|
||||
status: 'active',
|
||||
total_tuple_count: pgclass_info.cardinality_estimate)
|
||||
end
|
||||
|
||||
context 'when the job interval is lower than the minimum' do
|
||||
let(:minimum_delay) { described_class::BATCH_MIN_DELAY }
|
||||
|
||||
it 'sets the job interval to the minimum value' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: minimum_delay - 1.minute)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.interval).to eq(minimum_delay)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when additional arguments are passed to the method' do
|
||||
it 'saves the arguments on the database record' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration(
|
||||
'MyJobClass',
|
||||
:projects,
|
||||
:id,
|
||||
'my',
|
||||
'arguments',
|
||||
job_interval: 5.minutes,
|
||||
batch_max_value: 1000)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to have_attributes(
|
||||
job_class_name: 'MyJobClass',
|
||||
table_name: 'projects',
|
||||
column_name: 'id',
|
||||
interval: 300,
|
||||
min_value: 1,
|
||||
max_value: 1000,
|
||||
job_arguments: %w[my arguments])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the max_value is not given' do
|
||||
context 'when records exist in the database' do
|
||||
let!(:event1) { create(:event) }
|
||||
let!(:event2) { create(:event) }
|
||||
let!(:event3) { create(:event) }
|
||||
|
||||
it 'creates the record with the current max value' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.max_value).to eq(event3.id)
|
||||
end
|
||||
|
||||
it 'creates the record with an active status' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_active
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the database is empty' do
|
||||
it 'sets the max value to the min value' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration('MyJobClass', :events, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
created_migration = Gitlab::Database::BackgroundMigration::BatchedMigration.last
|
||||
|
||||
expect(created_migration.max_value).to eq(created_migration.min_value)
|
||||
end
|
||||
|
||||
it 'creates the record with a finished status' do
|
||||
expect do
|
||||
migration.queue_batched_background_migration('MyJobClass', :projects, :id, job_interval: 5.minutes)
|
||||
end.to change { Gitlab::Database::BackgroundMigration::BatchedMigration.count }.by(1)
|
||||
|
||||
expect(Gitlab::Database::BackgroundMigration::BatchedMigration.last).to be_finished
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue