Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue4
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue5
-rw-r--r--app/assets/javascripts/deploy_keys/components/action_btn.vue9
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue4
-rw-r--r--app/assets/javascripts/groups/components/app.vue4
-rw-r--r--app/assets/javascripts/groups/components/groups.vue7
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue2
-rw-r--r--app/assets/javascripts/helpers/event_hub_factory.js6
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue3
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js77
-rw-r--r--app/assets/javascripts/sidebar/event_hub.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue27
-rw-r--r--changelogs/unreleased/214824-fix-web-ide-open-file-race-condition.yml4
-rw-r--r--changelogs/unreleased/georgekoltsov-emit-bitbucket-server-metrics.yml5
-rw-r--r--doc/administration/geo/replication/multiple_servers.md6
-rw-r--r--doc/administration/high_availability/redis.md1007
-rw-r--r--doc/administration/high_availability/redis_source.md370
-rw-r--r--doc/administration/redis/index.md42
-rw-r--r--doc/administration/redis/replication_and_failover.md740
-rw-r--r--doc/administration/redis/replication_and_failover_external.md376
-rw-r--r--doc/administration/redis/standalone.md63
-rw-r--r--doc/administration/redis/troubleshooting.md158
-rw-r--r--doc/administration/reference_architectures/10k_users.md2
-rw-r--r--doc/administration/reference_architectures/1k_users.md2
-rw-r--r--doc/administration/reference_architectures/25k_users.md2
-rw-r--r--doc/administration/reference_architectures/3k_users.md2
-rw-r--r--doc/administration/reference_architectures/50k_users.md2
-rw-r--r--doc/administration/reference_architectures/5k_users.md2
-rw-r--r--doc/administration/reference_architectures/index.md2
-rw-r--r--doc/api/users.md3
-rw-r--r--doc/install/aws/index.md2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb11
-rw-r--r--lib/gitlab/bitbucket_import/metrics.rb41
-rw-r--r--lib/gitlab/bitbucket_server_import/importer.rb11
-rw-r--r--lib/gitlab/import/metrics.rb87
-rw-r--r--locale/gitlab.pot10
-rw-r--r--package.json6
-rw-r--r--spec/db/schema_spec.rb2
-rw-r--r--spec/frontend/boards/board_card_spec.js2
-rw-r--r--spec/frontend/cycle_analytics/stage_nav_item_spec.js3
-rw-r--r--spec/frontend/deploy_keys/components/action_btn_spec.js2
-rw-r--r--spec/frontend/deploy_keys/components/app_spec.js6
-rw-r--r--spec/frontend/groups/components/app_spec.js8
-rw-r--r--spec/frontend/groups/components/groups_spec.js5
-rw-r--r--spec/frontend/groups/components/item_actions_spec.js5
-rw-r--r--spec/frontend/helpers/wait_using_real_timer.js7
-rw-r--r--spec/frontend/ide/components/repo_editor_spec.js118
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js54
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js3
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js12
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb24
-rw-r--r--spec/lib/gitlab/bitbucket_server_import/importer_spec.rb49
-rw-r--r--spec/lib/gitlab/import/metrics_spec.rb68
-rw-r--r--yarn.lock18
55 files changed, 1831 insertions, 1665 deletions
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 246d3b9dcd1..e120e058ede 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -89,10 +89,10 @@ export default {
eventHub.$emit('clearDetailIssue', isMultiSelect);
if (isMultiSelect) {
- eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
+ eventHub.$emit('newDetailIssue', [this.issue, isMultiSelect]);
}
} else {
- eventHub.$emit('newDetailIssue', this.issue, isMultiSelect);
+ eventHub.$emit('newDetailIssue', [this.issue, isMultiSelect]);
boardsStore.setListDetail(this.list);
}
}
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index a882cd1cdfa..01e01ad6b8e 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -202,7 +202,7 @@ export default () => {
updateTokens() {
this.filterManager.updateTokens();
},
- updateDetailIssue(newIssue, multiSelect = false) {
+ updateDetailIssue([newIssue, multiSelect = false]) {
const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true);
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
index 3c18608eb75..4b15bd55cbd 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
@@ -25,11 +25,6 @@ export default {
default: '',
required: false,
},
- canEdit: {
- type: Boolean,
- default: false,
- required: false,
- },
},
computed: {
hasValue() {
diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue
index af7c391ab70..c2c90bd472b 100644
--- a/app/assets/javascripts/deploy_keys/components/action_btn.vue
+++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue
@@ -30,9 +30,12 @@ export default {
doAction() {
this.isLoading = true;
- eventHub.$emit(`${this.type}.key`, this.deployKey, () => {
- this.isLoading = false;
- });
+ eventHub.$emit(`${this.type}.key`, [
+ this.deployKey,
+ () => {
+ this.isLoading = false;
+ },
+ ]);
},
},
};
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 5505704f430..df31ee65b27 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -90,13 +90,13 @@ export default {
return new Flash(s__('DeployKeys|Error getting deploy keys'));
});
},
- enableKey(deployKey) {
+ enableKey([deployKey]) {
this.service
.enableKey(deployKey.id)
.then(this.fetchKeys)
.catch(() => new Flash(s__('DeployKeys|Error enabling deploy key')));
},
- disableKey(deployKey, callback) {
+ disableKey([deployKey, callback]) {
if (
// eslint-disable-next-line no-alert
window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index 0b401f4d732..0f2ee4a5224 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -123,7 +123,7 @@ export default {
this.updateGroups(res, Boolean(filterGroupsBy));
});
},
- fetchPage(page, filterGroupsBy, sortBy, archived) {
+ fetchPage([page, filterGroupsBy, sortBy, archived]) {
this.isLoading = true;
return this.fetchGroups({
@@ -169,7 +169,7 @@ export default {
parentGroup.isOpen = false;
}
},
- showLeaveGroupModal(group, parentGroup) {
+ showLeaveGroupModal([group, parentGroup]) {
const { fullName } = group;
this.targetGroup = group;
this.targetParentGroup = parentGroup;
diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue
index c7acc21378b..daf145d4eca 100644
--- a/app/assets/javascripts/groups/components/groups.vue
+++ b/app/assets/javascripts/groups/components/groups.vue
@@ -35,7 +35,12 @@ export default {
const filterGroupsParam = getParameterByName('filter');
const sortParam = getParameterByName('sort');
const archivedParam = getParameterByName('archived');
- eventHub.$emit(`${this.action}fetchPage`, page, filterGroupsParam, sortParam, archivedParam);
+ eventHub.$emit(`${this.action}fetchPage`, [
+ page,
+ filterGroupsParam,
+ sortParam,
+ archivedParam,
+ ]);
},
},
};
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 985ea5a9019..c91551da475 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -37,7 +37,7 @@ export default {
},
methods: {
onLeaveGroup() {
- eventHub.$emit(`${this.action}showLeaveGroupModal`, this.group, this.parentGroup);
+ eventHub.$emit(`${this.action}showLeaveGroupModal`, [this.group, this.parentGroup]);
},
},
};
diff --git a/app/assets/javascripts/helpers/event_hub_factory.js b/app/assets/javascripts/helpers/event_hub_factory.js
index 4d7f7550a94..08cfe40b52d 100644
--- a/app/assets/javascripts/helpers/event_hub_factory.js
+++ b/app/assets/javascripts/helpers/event_hub_factory.js
@@ -3,6 +3,8 @@ import mitt from 'mitt';
export default () => {
const emitter = mitt();
+ const originalEmit = emitter.emit;
+
emitter.once = (event, handler) => {
const wrappedHandler = evt => {
handler(evt);
@@ -11,6 +13,10 @@ export default () => {
emitter.on(event, wrappedHandler);
};
+ emitter.emit = (event, args = []) => {
+ originalEmit(event, args);
+ };
+
emitter.$on = emitter.on;
emitter.$once = emitter.once;
emitter.$off = emitter.off;
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index a7646083428..ac445a1d9f1 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -185,7 +185,6 @@ export default {
'setFileLanguage',
'setEditorPosition',
'setFileViewMode',
- 'updateViewer',
'removePendingTab',
'triggerFilesChange',
'addTempImage',
@@ -241,7 +240,7 @@ export default {
});
},
setupEditor() {
- if (!this.file || !this.editor.instance) return;
+ if (!this.file || !this.editor.instance || this.file.loading) return;
const head = this.getStagedFile(this.file.path);
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 47f9337a288..1ebf14d6eb7 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -65,7 +65,7 @@ export const getFileData = (
if (file.raw || (file.tempFile && !file.prevPath && !fileDeletedAndReadded))
return Promise.resolve();
- commit(types.TOGGLE_LOADING, { entry: file });
+ commit(types.TOGGLE_LOADING, { entry: file, forceValue: true });
const url = joinPaths(
gon.relative_url_root || '/',
@@ -84,10 +84,8 @@ export const getFileData = (
if (data) commit(types.SET_FILE_DATA, { data, file });
if (openFile) commit(types.TOGGLE_FILE_OPEN, path);
if (makeFileActive) dispatch('setFileActive', path);
- commit(types.TOGGLE_LOADING, { entry: file });
})
.catch(() => {
- commit(types.TOGGLE_LOADING, { entry: file });
dispatch('setErrorMessage', {
text: __('An error occurred while loading the file.'),
action: payload =>
@@ -95,6 +93,9 @@ export const getFileData = (
actionText: __('Please try again'),
actionPayload: { path, makeFileActive },
});
+ })
+ .finally(() => {
+ commit(types.TOGGLE_LOADING, { entry: file, forceValue: false });
});
};
@@ -106,45 +107,41 @@ export const getRawFileData = ({ state, commit, dispatch, getters }, { path }) =
const file = state.entries[path];
const stagedFile = state.stagedFiles.find(f => f.path === path);
- return new Promise((resolve, reject) => {
- const fileDeletedAndReadded = getters.isFileDeletedAndReadded(path);
- service
- .getRawFileData(fileDeletedAndReadded ? stagedFile : file)
- .then(raw => {
- if (!(file.tempFile && !file.prevPath && !fileDeletedAndReadded))
- commit(types.SET_FILE_RAW_DATA, { file, raw, fileDeletedAndReadded });
-
- if (file.mrChange && file.mrChange.new_file === false) {
- const baseSha =
- (getters.currentMergeRequest && getters.currentMergeRequest.baseCommitSha) || '';
-
- service
- .getBaseRawFileData(file, baseSha)
- .then(baseRaw => {
- commit(types.SET_FILE_BASE_RAW_DATA, {
- file,
- baseRaw,
- });
- resolve(raw);
- })
- .catch(e => {
- reject(e);
- });
- } else {
- resolve(raw);
- }
- })
- .catch(() => {
- dispatch('setErrorMessage', {
- text: __('An error occurred while loading the file content.'),
- action: payload =>
- dispatch('getRawFileData', payload).then(() => dispatch('setErrorMessage', null)),
- actionText: __('Please try again'),
- actionPayload: { path },
+ const fileDeletedAndReadded = getters.isFileDeletedAndReadded(path);
+ commit(types.TOGGLE_LOADING, { entry: file, forceValue: true });
+ return service
+ .getRawFileData(fileDeletedAndReadded ? stagedFile : file)
+ .then(raw => {
+ if (!(file.tempFile && !file.prevPath && !fileDeletedAndReadded))
+ commit(types.SET_FILE_RAW_DATA, { file, raw, fileDeletedAndReadded });
+
+ if (file.mrChange && file.mrChange.new_file === false) {
+ const baseSha =
+ (getters.currentMergeRequest && getters.currentMergeRequest.baseCommitSha) || '';
+
+ return service.getBaseRawFileData(file, baseSha).then(baseRaw => {
+ commit(types.SET_FILE_BASE_RAW_DATA, {
+ file,
+ baseRaw,
+ });
+ return raw;
});
- reject();
+ }
+ return raw;
+ })
+ .catch(e => {
+ dispatch('setErrorMessage', {
+ text: __('An error occurred while loading the file content.'),
+ action: payload =>
+ dispatch('getRawFileData', payload).then(() => dispatch('setErrorMessage', null)),
+ actionText: __('Please try again'),
+ actionPayload: { path },
});
- });
+ throw e;
+ })
+ .finally(() => {
+ commit(types.TOGGLE_LOADING, { entry: file, forceValue: false });
+ });
};
export const changeFileContent = ({ commit, state, getters }, { path, content }) => {
diff --git a/app/assets/javascripts/sidebar/event_hub.js b/app/assets/javascripts/sidebar/event_hub.js
index f35506fd5de..dd4bd9a5ab7 100644
--- a/app/assets/javascripts/sidebar/event_hub.js
+++ b/app/assets/javascripts/sidebar/event_hub.js
@@ -1,6 +1,6 @@
-import Vue from 'vue';
+import createEventHub from '~/helpers/event_hub_factory';
-const eventHub = new Vue();
+const eventHub = createEventHub();
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = (...args) => eventHub.$emit(...args);
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue
index f6bfb178437..1a2d845fab9 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue
@@ -1,9 +1,10 @@
<script>
-import { GlPopover, GlDeprecatedButton } from '@gitlab/ui';
+import { GlPopover, GlDeprecatedButton, GlSprintf, GlLink } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import Cookies from 'js-cookie';
import { parseBoolean } from '~/lib/utils/common_utils';
import Tracking from '~/tracking';
+import { s__ } from '~/locale';
const trackingMixin = Tracking.mixin();
@@ -14,10 +15,16 @@ export default {
dismissTrackValue: 20,
showTrackValue: 10,
trackEvent: 'click_button',
+ helpContent: s__(
+ `mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd}, simply add a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust.`,
+ ),
+ helpURL: 'https://about.gitlab.com/blog/2019/07/12/guide-to-ci-cd-pipelines/',
components: {
GlPopover,
GlDeprecatedButton,
Icon,
+ GlSprintf,
+ GlLink,
},
mixins: [trackingMixin],
props: {
@@ -97,13 +104,13 @@ export default {
<div class="svg-content svg-150 pt-1">
<img :src="pipelineSvgPath" />
</div>
- <p>
- {{
- s__(
- 'mrWidget|Detect issues before deployment with a CI pipeline that continuously tests your code. We created a quick guide that will show you how to create one. Make your code more secure and more robust in just a minute.',
- )
- }}
- </p>
+ <gl-sprintf :message="$options.helpContent">
+ <template #link="{ content }">
+ <gl-link :href="$options.helpURL" target="_blank" class="font-size-inherit">{{
+ content
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
<gl-deprecated-button
ref="ok"
category="primary"
@@ -116,7 +123,7 @@ export default {
:data-track-event="$options.trackEvent"
:data-track-label="trackLabel"
>
- {{ __('Show me how') }}
+ {{ __('Show me how to add a pipeline') }}
</gl-deprecated-button>
<gl-deprecated-button
ref="no-thanks"
@@ -130,7 +137,7 @@ export default {
:data-track-label="trackLabel"
@click="dismissPopover"
>
- {{ __("No thanks, don't show this again") }}
+ {{ __('No thanks') }}
</gl-deprecated-button>
</gl-popover>
</template>
diff --git a/changelogs/unreleased/214824-fix-web-ide-open-file-race-condition.yml b/changelogs/unreleased/214824-fix-web-ide-open-file-race-condition.yml
new file mode 100644
index 00000000000..c390e805514
--- /dev/null
+++ b/changelogs/unreleased/214824-fix-web-ide-open-file-race-condition.yml
@@ -0,0 +1,4 @@
+---
+title: Resolve "WebIDE displays blank file incorrectly"
+merge_request: 33391
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-emit-bitbucket-server-metrics.yml b/changelogs/unreleased/georgekoltsov-emit-bitbucket-server-metrics.yml
new file mode 100644
index 00000000000..6f43b37bbae
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-emit-bitbucket-server-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Emit Bitbucket Server Importer metrics
+merge_request: 33700
+author:
+type: changed
diff --git a/doc/administration/geo/replication/multiple_servers.md b/doc/administration/geo/replication/multiple_servers.md
index 2747aaeb105..7cab9464e4a 100644
--- a/doc/administration/geo/replication/multiple_servers.md
+++ b/doc/administration/geo/replication/multiple_servers.md
@@ -46,7 +46,7 @@ for PostgreSQL and Redis, it is not covered by this Geo multi-server documentati
For more information about setting up a multi-server PostgreSQL cluster and Redis cluster using the omnibus package see the multi-server documentation for
[PostgreSQL](../../postgresql/replication_and_failover.md) and
-[Redis](../../high_availability/redis.md), respectively.
+[Redis](../../redis/replication_and_failover.md), respectively.
NOTE: **Note:**
It is possible to use cloud hosted services for PostgreSQL and Redis, but this is beyond the scope of this document.
@@ -95,7 +95,7 @@ application servers, and connections from the application servers to those
services on the backend servers configured, during normal GitLab multi-server set up. See
multi-server configuration documentation for
[PostgreSQL](../../postgresql/replication_and_failover.md#configuring-the-application-nodes)
-and [Redis](../../high_availability/redis.md#example-configuration-for-the-gitlab-application).
+and [Redis](../../redis/replication_and_failover.md#example-configuration-for-the-gitlab-application).
### Step 2: Configure the **primary** database
@@ -131,7 +131,7 @@ and are not related to Geo setup.
Configure the following services, again using the non-Geo multi-server
documentation:
-- [Configuring Redis for GitLab](../../high_availability/redis.md) for multiple servers.
+- [Configuring Redis for GitLab](../../redis/replication_and_failover.md#example-configuration-for-the-gitlab-application) for multiple servers.
- [Gitaly](../../high_availability/gitaly.md), which will store data that is
synchronized from the **primary** node.
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index bad50f7ca74..2b5771f49f2 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -1,1008 +1,5 @@
---
-type: reference
+redirect_to: ../redis/index.md
---
-# Configuring Redis for Scaling and High Availability
-
-## Provide your own Redis instance **(CORE ONLY)**
-
-The following are the requirements for providing your own Redis instance:
-
-- Redis version 5.0 or higher is recommended, as this is what ships with
- Omnibus GitLab packages starting with GitLab 12.7.
-- Support for Redis 3.2 is deprecated with GitLab 12.10 and will be completely
- removed in GitLab 13.0.
-- GitLab 12.0 and later requires Redis version 3.2 or higher. Older Redis
- versions do not support an optional count argument to SPOP which is now
- required for [Merge Trains](../../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md).
-- In addition, if Redis 4 or later is available, GitLab makes use of certain
- commands like `UNLINK` and `USAGE` which were introduced only in Redis 4.
-- Standalone Redis or Redis high availability with Sentinel are supported. Redis
- Cluster is not supported.
-- Managed Redis from cloud providers such as AWS ElastiCache will work. If these
- services support high availability, be sure it is not the Redis Cluster type.
-
-Note the Redis node's IP address or hostname, port, and password (if required).
-These will be necessary when configuring the GitLab application servers later.
-
-## Redis in a Scaled and Highly Available Environment
-
-This section is relevant for [scalable and highly available setups](../reference_architectures/index.md).
-
-### Provide your own Redis instance **(CORE ONLY)**
-
-If you want to use your own deployed Redis instance(s),
-see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
-for more details. However, you can use the Omnibus GitLab package to easily
-deploy the bundled Redis.
-
-### Standalone Redis using Omnibus GitLab **(CORE ONLY)**
-
-The Omnibus GitLab package can be used to configure a standalone Redis server.
-In this configuration Redis is not highly available, and represents a single
-point of failure. However, in a scaled environment the objective is to allow
-the environment to handle more users or to increase throughput. Redis itself
-is generally stable and can handle many requests so it is an acceptable
-trade off to have only a single instance. See the [reference architectures](../reference_architectures/index.md)
-page for an overview of GitLab scaling and high availability options.
-
-The steps below are the minimum necessary to configure a Redis server with
-Omnibus:
-
-1. SSH into the Redis server.
-1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
- package you want using **steps 1 and 2** from the GitLab downloads page.
- - Do not complete any other steps on the download page.
-
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- ## Enable Redis
- redis['enable'] = true
-
- ## Disable all other services
- sidekiq['enable'] = false
- gitlab_workhorse['enable'] = false
- puma['enable'] = false
- postgresql['enable'] = false
- nginx['enable'] = false
- prometheus['enable'] = false
- alertmanager['enable'] = false
- pgbouncer_exporter['enable'] = false
- gitlab_exporter['enable'] = false
- gitaly['enable'] = false
-
- redis['bind'] = '0.0.0.0'
- redis['port'] = 6379
- redis['password'] = 'SECRET_PASSWORD_HERE'
-
- gitlab_rails['enable'] = false
- ```
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Note the Redis node's IP address or hostname, port, and
- Redis password. These will be necessary when configuring the GitLab
- application servers later.
-1. [Enable Monitoring](#enable-monitoring)
-
-Advanced configuration options are supported and can be added if
-needed.
-
-Continue configuration of other components by going back to the
-[reference architectures](../reference_architectures/index.md#configure-gitlab-to-scale) page.
-
-### High Availability with Omnibus GitLab **(PREMIUM ONLY)**
-
-> Experimental Redis Sentinel support was [introduced in GitLab 8.11](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1877).
-Starting with 8.14, Redis Sentinel is no longer experimental.
-If you've used it with versions `< 8.14` before, please check the updated
-documentation here.
-
-High Availability with [Redis](https://redis.io/) is possible using a **Master** x **Replica**
-topology with a [Redis Sentinel](https://redis.io/topics/sentinel) service to watch and automatically
-start the failover procedure.
-
-You can choose to install and manage Redis and Sentinel yourself, use
-a hosted cloud solution or you can use the one that comes bundled with
-Omnibus GitLab packages.
-
-> **Notes:**
->
-> - Redis requires authentication for High Availability. See
-> [Redis Security](https://redis.io/topics/security) documentation for more
-> information. We recommend using a combination of a Redis password and tight
-> firewall rules to secure your Redis service.
-> - You are highly encouraged to read the [Redis Sentinel](https://redis.io/topics/sentinel) documentation
-> before configuring Redis HA with GitLab to fully understand the topology and
-> architecture.
-> - This is the documentation for the Omnibus GitLab packages. For installations
-> from source, follow the [Redis HA source installation](redis_source.md) guide.
-> - Redis Sentinel daemon is bundled with Omnibus GitLab Enterprise Edition only.
-> For configuring Sentinel with the Omnibus GitLab Community Edition and
-> installations from source, read the
-> [Available configuration setups](#available-configuration-setups) section
-> below.
-
-## Overview
-
-Before diving into the details of setting up Redis and Redis Sentinel for HA,
-make sure you read this Overview section to better understand how the components
-are tied together.
-
-You need at least `3` independent machines: physical, or VMs running into
-distinct physical machines. It is essential that all master and replica Redis
-instances run in different machines. If you fail to provision the machines in
-that specific way, any issue with the shared environment can bring your entire
-setup down.
-
-It is OK to run a Sentinel alongside of a master or replica Redis instance.
-There should be no more than one Sentinel on the same machine though.
-
-You also need to take into consideration the underlying network topology,
-making sure you have redundant connectivity between Redis / Sentinel and
-GitLab instances, otherwise the networks will become a single point of
-failure.
-
-Make sure that you read this document once as a whole before configuring the
-components below.
-
-> **Notes:**
->
-> - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel
-> servers that will monitor a group of Redis servers to provide failover support.
-> - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package
-> comes with Redis Sentinel daemon built-in.
-
-High Availability with Redis requires a few things:
-
-- Multiple Redis instances
-- Run Redis in a **Master** x **Replica** topology
-- Multiple Sentinel instances
-- Application support and visibility to all Sentinel and Redis instances
-
-Redis Sentinel can handle the most important tasks in an HA environment and that's
-to help keep servers online with minimal to no downtime. Redis Sentinel:
-
-- Monitors **Master** and **Replicas** instances to see if they are available
-- Promotes a **Replica** to **Master** when the **Master** fails
-- Demotes a **Master** to **Replica** when the failed **Master** comes back online
- (to prevent data-partitioning)
-- Can be queried by the application to always connect to the current **Master**
- server
-
-When a **Master** fails to respond, it's the application's responsibility
-(in our case GitLab) to handle timeout and reconnect (querying a **Sentinel**
-for a new **Master**).
-
-To get a better understanding on how to correctly set up Sentinel, please read
-the [Redis Sentinel documentation](https://redis.io/topics/sentinel) first, as
-failing to configure it correctly can lead to data loss or can bring your
-whole cluster down, invalidating the failover effort.
-
-### Recommended setup
-
-For a minimal setup, you will install the Omnibus GitLab package in `3`
-**independent** machines, both with **Redis** and **Sentinel**:
-
-- Redis Master + Sentinel
-- Redis Replica + Sentinel
-- Redis Replica + Sentinel
-
-If you are not sure or don't understand why and where the amount of nodes come
-from, read [Redis setup overview](#redis-setup-overview) and
-[Sentinel setup overview](#sentinel-setup-overview).
-
-For a recommended setup that can resist more failures, you will install
-the Omnibus GitLab package in `5` **independent** machines, both with
-**Redis** and **Sentinel**:
-
-- Redis Master + Sentinel
-- Redis Replica + Sentinel
-- Redis Replica + Sentinel
-- Redis Replica + Sentinel
-- Redis Replica + Sentinel
-
-### Redis setup overview
-
-You must have at least `3` Redis servers: `1` Master, `2` Replicas, and they
-need to each be on independent machines (see explanation above).
-
-You can have additional Redis nodes, that will help survive a situation
-where more nodes goes down. Whenever there is only `2` nodes online, a failover
-will not be initiated.
-
-As an example, if you have `6` Redis nodes, a maximum of `3` can be
-simultaneously down.
-
-Please note that there are different requirements for Sentinel nodes.
-If you host them in the same Redis machines, you may need to take
-that restrictions into consideration when calculating the amount of
-nodes to be provisioned. See [Sentinel setup overview](#sentinel-setup-overview)
-documentation for more information.
-
-All Redis nodes should be configured the same way and with similar server specs, as
-in a failover situation, any **Replica** can be promoted as the new **Master** by
-the Sentinel servers.
-
-The replication requires authentication, so you need to define a password to
-protect all Redis nodes and the Sentinels. They will all share the same
-password, and all instances must be able to talk to
-each other over the network.
-
-### Sentinel setup overview
-
-Sentinels watch both other Sentinels and Redis nodes. Whenever a Sentinel
-detects that a Redis node is not responding, it will announce that to the
-other Sentinels. They have to reach the **quorum**, that is the minimum amount
-of Sentinels that agrees a node is down, in order to be able to start a failover.
-
-Whenever the **quorum** is met, the **majority** of all known Sentinel nodes
-need to be available and reachable, so that they can elect the Sentinel **leader**
-who will take all the decisions to restore the service availability by:
-
-- Promoting a new **Master**
-- Reconfiguring the other **Replicas** and make them point to the new **Master**
-- Announce the new **Master** to every other Sentinel peer
-- Reconfigure the old **Master** and demote to **Replica** when it comes back online
-
-You must have at least `3` Redis Sentinel servers, and they need to
-be each in an independent machine (that are believed to fail independently),
-ideally in different geographical areas.
-
-You can configure them in the same machines where you've configured the other
-Redis servers, but understand that if a whole node goes down, you loose both
-a Sentinel and a Redis instance.
-
-The number of sentinels should ideally always be an **odd** number, for the
-consensus algorithm to be effective in the case of a failure.
-
-In a `3` nodes topology, you can only afford `1` Sentinel node going down.
-Whenever the **majority** of the Sentinels goes down, the network partition
-protection prevents destructive actions and a failover **will not be started**.
-
-Here are some examples:
-
-- With `5` or `6` sentinels, a maximum of `2` can go down for a failover begin.
-- With `7` sentinels, a maximum of `3` nodes can go down.
-
-The **Leader** election can sometimes fail the voting round when **consensus**
-is not achieved (see the odd number of nodes requirement above). In that case,
-a new attempt will be made after the amount of time defined in
-`sentinel['failover_timeout']` (in milliseconds).
-
->**Note:**
-We will see where `sentinel['failover_timeout']` is defined later.
-
-The `failover_timeout` variable has a lot of different use cases. According to
-the official documentation:
-
-- The time needed to re-start a failover after a previous failover was
- already tried against the same master by a given Sentinel, is two
- times the failover timeout.
-
-- The time needed for a replica replicating to a wrong master according
- to a Sentinel current configuration, to be forced to replicate
- with the right master, is exactly the failover timeout (counting since
- the moment a Sentinel detected the misconfiguration).
-
-- The time needed to cancel a failover that is already in progress but
- did not produced any configuration change (REPLICAOF NO ONE yet not
- acknowledged by the promoted replica).
-
-- The maximum time a failover in progress waits for all the replicas to be
- reconfigured as replicas of the new master. However even after this time
- the replicas will be reconfigured by the Sentinels anyway, but not with
- the exact parallel-syncs progression as specified.
-
-### Available configuration setups
-
-Based on your infrastructure setup and how you have installed GitLab, there are
-multiple ways to configure Redis HA. Omnibus GitLab packages have Redis and/or
-Redis Sentinel bundled with them so you only need to focus on configuration.
-Pick the one that suits your needs.
-
-- [Installations from source](../../install/installation.md): You need to install Redis and Sentinel
- yourself. Use the [Redis HA installation from source](redis_source.md)
- documentation.
-- [Omnibus GitLab **Community Edition** (CE) package](https://about.gitlab.com/install/?version=ce): Redis is bundled, so you
- can use the package with only the Redis service enabled as described in steps
- 1 and 2 of this document (works for both master and replica setups). To install
- and configure Sentinel, jump directly to the Sentinel section in the
- [Redis HA installation from source](redis_source.md#step-3-configuring-the-redis-sentinel-instances) documentation.
-- [Omnibus GitLab **Enterprise Edition** (EE) package](https://about.gitlab.com/install/?version=ee): Both Redis and Sentinel
- are bundled in the package, so you can use the EE package to set up the whole
- Redis HA infrastructure (master, replica and Sentinel) which is described in
- this document.
-- If you have installed GitLab using the Omnibus GitLab packages (CE or EE),
- but you want to use your own external Redis server, follow steps 1-3 in the
- [Redis HA installation from source](redis_source.md) documentation, then go
- straight to step 4 in this guide to
- [set up the GitLab application](#step-4-configuring-the-gitlab-application).
-
-## Configuring Redis HA
-
-This is the section where we install and set up the new Redis instances.
-
-> **Notes:**
->
-> - We assume that you have installed GitLab and all HA components from scratch. If you
-> already have it installed and running, read how to
-> [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
-> - Redis nodes (both master and replica) will need the same password defined in
-> `redis['password']`. At any time during a failover the Sentinels can
-> reconfigure a node and change its status from master to replica and vice versa.
-
-### Prerequisites
-
-The prerequisites for a HA Redis setup are the following:
-
-1. Provision the minimum required number of instances as specified in the
- [recommended setup](#recommended-setup) section.
-1. We **Do not** recommend installing Redis or Redis Sentinel in the same machines your
- GitLab application is running on as this weakens your HA configuration. You can however opt in to install Redis
- and Sentinel in the same machine.
-1. All Redis nodes must be able to talk to each other and accept incoming
- connections over Redis (`6379`) and Sentinel (`26379`) ports (unless you
- change the default ones).
-1. The server that hosts the GitLab application must be able to access the
- Redis nodes.
-1. Protect the nodes from access from external networks ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)), using
- firewall.
-
-### Step 1. Configuring the master Redis instance
-
-1. SSH into the **master** Redis server.
-1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
- package you want using **steps 1 and 2** from the GitLab downloads page.
- - Make sure you select the correct Omnibus package, with the same version
- and type (Community, Enterprise editions) of your current install.
- - Do not complete any other steps on the download page.
-
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- # Specify server role as 'redis_master_role'
- roles ['redis_master_role']
-
- # IP address pointing to a local IP that the other machines can reach to.
- # You can also set bind to '0.0.0.0' which listen in all interfaces.
- # If you really need to bind to an external accessible IP, make
- # sure you add extra firewall rules to prevent unauthorized access.
- redis['bind'] = '10.0.0.1'
-
- # Define a port so Redis can listen for TCP requests which will allow other
- # machines to connect to it.
- redis['port'] = 6379
-
- # Set up password authentication for Redis (use the same password in all nodes).
- redis['password'] = 'redis-password-goes-here'
- ```
-
-1. Only the primary GitLab application server should handle migrations. To
- prevent database migrations from running on upgrade, add the following
- configuration to your `/etc/gitlab/gitlab.rb` file:
-
- ```ruby
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-> Note: You can specify multiple roles like sentinel and Redis as:
-> `roles ['redis_sentinel_role', 'redis_master_role']`. Read more about high
-> availability roles at <https://docs.gitlab.com/omnibus/roles/>.
-
-### Step 2. Configuring the replica Redis instances
-
-1. SSH into the **replica** Redis server.
-1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
- package you want using **steps 1 and 2** from the GitLab downloads page.
- - Make sure you select the correct Omnibus package, with the same version
- and type (Community, Enterprise editions) of your current install.
- - Do not complete any other steps on the download page.
-
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- # Specify server role as 'redis_replica_role'
- roles ['redis_replica_role']
-
- # IP address pointing to a local IP that the other machines can reach to.
- # You can also set bind to '0.0.0.0' which listen in all interfaces.
- # If you really need to bind to an external accessible IP, make
- # sure you add extra firewall rules to prevent unauthorized access.
- redis['bind'] = '10.0.0.2'
-
- # Define a port so Redis can listen for TCP requests which will allow other
- # machines to connect to it.
- redis['port'] = 6379
-
- # The same password for Redis authentication you set up for the master node.
- redis['password'] = 'redis-password-goes-here'
-
- # The IP of the master Redis node.
- redis['master_ip'] = '10.0.0.1'
-
- # Port of master Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
- ```
-
-1. To prevent reconfigure from running automatically on upgrade, run:
-
- ```shell
- sudo touch /etc/gitlab/skip-auto-reconfigure
- ```
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Go through the steps again for all the other replica nodes.
-
-> Note: You can specify multiple roles like sentinel and Redis as:
-> `roles ['redis_sentinel_role', 'redis_replica_role']`. Read more about high
-> availability roles at <https://docs.gitlab.com/omnibus/roles/>.
-
----
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the Sentinels, and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
-### Step 3. Configuring the Redis Sentinel instances
-
->**Note:**
-Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. The
-following section assumes you are using Omnibus GitLab Enterprise Edition.
-For the Omnibus Community Edition and installations from source, follow the
-[Redis HA source install](redis_source.md) guide.
-
-NOTE: **Note:** If you are using an external Redis Sentinel instance, be sure
-to exclude the `requirepass` parameter from the Sentinel
-configuration. This parameter will cause clients to report `NOAUTH
-Authentication required.`. [Redis Sentinel 3.2.x does not support
-password authentication](https://github.com/antirez/redis/issues/3279).
-
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers.
-
-If you are not sure if your Redis servers are working and replicating
-correctly, please read the [Troubleshooting Replication](#troubleshooting-redis-replication)
-and fix it before proceeding with Sentinel setup.
-
-You must have at least `3` Redis Sentinel servers, and they need to
-be each in an independent machine. You can configure them in the same
-machines where you've configured the other Redis servers.
-
-With GitLab Enterprise Edition, you can use the Omnibus package to set up
-multiple machines with the Sentinel daemon.
-
----
-
-1. SSH into the server that will host Redis Sentinel.
-1. **You can omit this step if the Sentinels will be hosted in the same node as
- the other Redis instances.**
-
- [Download/install](https://about.gitlab.com/install/) the
- Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the
- GitLab downloads page.
- - Make sure you select the correct Omnibus package, with the same version
- the GitLab application is running.
- - Do not complete any other steps on the download page.
-
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents (if you are installing the
- Sentinels in the same node as the other Redis instances, some values might
- be duplicate below):
-
- ```ruby
- roles ['redis_sentinel_role']
-
- # Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis'
-
- # The same password for Redis authentication you set up for the master node.
- redis['master_password'] = 'redis-password-goes-here'
-
- # The IP of the master Redis node.
- redis['master_ip'] = '10.0.0.1'
-
- # Define a port so Redis can listen for TCP requests which will allow other
- # machines to connect to it.
- redis['port'] = 6379
-
- # Port of master Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel
- sentinel['bind'] = '10.0.0.1'
-
- # Port that Sentinel listens on, uncomment to change to non default. Defaults
- # to `26379`.
- # sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to master failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the master.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the master being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- # sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same master by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong master according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right master, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new master. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- # sentinel['failover_timeout'] = 60000
- ```
-
-1. To prevent database migrations from running on upgrade, run:
-
- ```shell
- sudo touch /etc/gitlab/skip-auto-reconfigure
- ```
-
- Only the primary GitLab application server should handle migrations.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Go through the steps again for all the other Sentinel nodes.
-
-### Step 4. Configuring the GitLab application
-
-The final part is to inform the main GitLab application server of the Redis
-Sentinels servers and authentication credentials.
-
-You can enable or disable Sentinel support at any time in new or existing
-installations. From the GitLab application perspective, all it requires is
-the correct credentials for the Sentinel nodes.
-
-While it doesn't require a list of all Sentinel nodes, in case of a failure,
-it needs to access at least one of the listed.
-
->**Note:**
-The following steps should be performed in the [GitLab application server](gitlab.md)
-which ideally should not have Redis or Sentinels on it for a HA setup.
-
-1. SSH into the server where the GitLab application is installed.
-1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines:
-
- ```ruby
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis'
-
- ## The same password for Redis authentication you set up for the master node.
- redis['master_password'] = 'redis-password-goes-here'
-
- ## A list of sentinels with `host` and `port`
- gitlab_rails['redis_sentinels'] = [
- {'host' => '10.0.0.1', 'port' => 26379},
- {'host' => '10.0.0.2', 'port' => 26379},
- {'host' => '10.0.0.3', 'port' => 26379}
- ]
- ```
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-## Switching from an existing single-machine installation to Redis HA
-
-If you already have a single-machine GitLab install running, you will need to
-replicate from this machine first, before de-activating the Redis instance
-inside it.
-
-Your single-machine install will be the initial **Master**, and the `3` others
-should be configured as **Replica** pointing to this machine.
-
-After replication catches up, you will need to stop services in the
-single-machine install, to rotate the **Master** to one of the new nodes.
-
-Make the required changes in configuration and restart the new nodes again.
-
-To disable Redis in the single install, edit `/etc/gitlab/gitlab.rb`:
-
-```ruby
-redis['enable'] = false
-```
-
-If you fail to replicate first, you may loose data (unprocessed background jobs).
-
-## Example of a minimal configuration with 1 master, 2 replicas and 3 Sentinels
-
->**Note:**
-Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For
-different setups, read the
-[available configuration setups](#available-configuration-setups) section.
-
-In this example we consider that all servers have an internal network
-interface with IPs in the `10.0.0.x` range, and that they can connect
-to each other using these IPs.
-
-In a real world usage, you would also set up firewall rules to prevent
-unauthorized access from other machines and block traffic from the
-outside (Internet).
-
-We will use the same `3` nodes with **Redis** + **Sentinel** topology
-discussed in [Redis setup overview](#redis-setup-overview) and
-[Sentinel setup overview](#sentinel-setup-overview) documentation.
-
-Here is a list and description of each **machine** and the assigned **IP**:
-
-- `10.0.0.1`: Redis Master + Sentinel 1
-- `10.0.0.2`: Redis Replica 1 + Sentinel 2
-- `10.0.0.3`: Redis Replica 2 + Sentinel 3
-- `10.0.0.4`: GitLab application
-
-Please note that after the initial configuration, if a failover is initiated
-by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master**
-will change permanently (including in `redis.conf`) from one node to the other,
-until a new failover is initiated again.
-
-The same thing will happen with `sentinel.conf` that will be overridden after the
-initial execution, after any new sentinel node starts watching the **Master**,
-or a failover promotes a different **Master** node.
-
-### Example configuration for Redis master and Sentinel 1
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-roles ['redis_sentinel_role', 'redis_master_role']
-redis['bind'] = '10.0.0.1'
-redis['port'] = 6379
-redis['password'] = 'redis-password-goes-here'
-redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
-redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the master instance
-redis['master_ip'] = '10.0.0.1' # ip of the initial master redis instance
-#redis['master_port'] = 6379 # port of the initial master redis instance, uncomment to change to non default
-sentinel['bind'] = '10.0.0.1'
-# sentinel['port'] = 26379 # uncomment to change default port
-sentinel['quorum'] = 2
-# sentinel['down_after_milliseconds'] = 10000
-# sentinel['failover_timeout'] = 60000
-```
-
-[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-### Example configuration for Redis replica 1 and Sentinel 2
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-roles ['redis_sentinel_role', 'redis_replica_role']
-redis['bind'] = '10.0.0.2'
-redis['port'] = 6379
-redis['password'] = 'redis-password-goes-here'
-redis['master_password'] = 'redis-password-goes-here'
-redis['master_ip'] = '10.0.0.1' # IP of master Redis server
-#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default
-redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
-sentinel['bind'] = '10.0.0.2'
-# sentinel['port'] = 26379 # uncomment to change default port
-sentinel['quorum'] = 2
-# sentinel['down_after_milliseconds'] = 10000
-# sentinel['failover_timeout'] = 60000
-```
-
-[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-### Example configuration for Redis replica 2 and Sentinel 3
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-roles ['redis_sentinel_role', 'redis_replica_role']
-redis['bind'] = '10.0.0.3'
-redis['port'] = 6379
-redis['password'] = 'redis-password-goes-here'
-redis['master_password'] = 'redis-password-goes-here'
-redis['master_ip'] = '10.0.0.1' # IP of master Redis server
-#redis['master_port'] = 6379 # Port of master Redis server, uncomment to change to non default
-redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
-sentinel['bind'] = '10.0.0.3'
-# sentinel['port'] = 26379 # uncomment to change default port
-sentinel['quorum'] = 2
-# sentinel['down_after_milliseconds'] = 10000
-# sentinel['failover_timeout'] = 60000
-```
-
-[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-### Example configuration for the GitLab application
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-redis['master_name'] = 'gitlab-redis'
-redis['master_password'] = 'redis-password-goes-here'
-gitlab_rails['redis_sentinels'] = [
- {'host' => '10.0.0.1', 'port' => 26379},
- {'host' => '10.0.0.2', 'port' => 26379},
- {'host' => '10.0.0.3', 'port' => 26379}
-]
-```
-
-[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-## Enable Monitoring
-
-> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3786) in GitLab 12.0.
-
-If you enable Monitoring, it must be enabled on **all** Redis servers.
-
-1. Make sure to collect [`CONSUL_SERVER_NODES`](../postgresql/replication_and_failover.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
-
-1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
-
- ```ruby
- # Enable service discovery for Prometheus
- consul['enable'] = true
- consul['monitoring_service_discovery'] = true
-
- # Replace placeholders
- # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
- # with the addresses of the Consul server nodes
- consul['configuration'] = {
- retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
- ```
-
-1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
-
-## Advanced configuration
-
-Omnibus GitLab configures some things behind the curtains to make the sysadmins'
-lives easier. If you want to know what happens underneath keep reading.
-
-### Running multiple Redis clusters
-
-GitLab supports running [separate Redis clusters for different persistent
-classes](https://docs.gitlab.com/omnibus/settings/redis.html#running-with-multiple-redis-instances):
-cache, queues, and shared_state. To make this work with Sentinel:
-
-1. Set the appropriate variable in `/etc/gitlab/gitlab.rb` for each instance you are using:
-
- ```ruby
- gitlab_rails['redis_cache_instance'] = REDIS_CACHE_URL
- gitlab_rails['redis_queues_instance'] = REDIS_QUEUES_URL
- gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL
- ```
-
- **Note**: Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_MASTER_NAME`
-
- 1. PASSWORD is the plaintext password for the Redis instance
- 1. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`)
-
-1. Include an array of hashes with host/port combinations, such as the following:
-
- ```ruby
- gitlab_rails['redis_cache_sentinels'] = [
- { host: REDIS_CACHE_SENTINEL_HOST, port: PORT1 },
- { host: REDIS_CACHE_SENTINEL_HOST2, port: PORT2 }
- ]
- gitlab_rails['redis_queues_sentinels'] = [
- { host: REDIS_QUEUES_SENTINEL_HOST, port: PORT1 },
- { host: REDIS_QUEUES_SENTINEL_HOST2, port: PORT2 }
- ]
- gitlab_rails['redis_shared_state_sentinels'] = [
- { host: SHARED_STATE_SENTINEL_HOST, port: PORT1 },
- { host: SHARED_STATE_SENTINEL_HOST2, port: PORT2 }
- ]
- ```
-
-1. Note that for each persistence class, GitLab will default to using the
- configuration specified in `gitlab_rails['redis_sentinels']` unless
- overridden by the settings above.
-1. Be sure to include BOTH configuration options for each persistent classes. For example,
- if you choose to configure a cache instance, you must specify both `gitlab_rails['redis_cache_instance']`
- and `gitlab_rails['redis_cache_sentinels']` for GitLab to generate the proper configuration files.
-1. Run `gitlab-ctl reconfigure`
-
-### Control running services
-
-In the previous example, we've used `redis_sentinel_role` and
-`redis_master_role` which simplifies the amount of configuration changes.
-
-If you want more control, here is what each one sets for you automatically
-when enabled:
-
-```ruby
-## Redis Sentinel Role
-redis_sentinel_role['enable'] = true
-
-# When Sentinel Role is enabled, the following services are also enabled
-sentinel['enable'] = true
-
-# The following services are disabled
-redis['enable'] = false
-bootstrap['enable'] = false
-nginx['enable'] = false
-postgresql['enable'] = false
-gitlab_rails['enable'] = false
-mailroom['enable'] = false
-
--------
-
-## Redis master/replica Role
-redis_master_role['enable'] = true # enable only one of them
-redis_replica_role['enable'] = true # enable only one of them
-
-# When Redis Master or Replica role are enabled, the following services are
-# enabled/disabled. Note that if Redis and Sentinel roles are combined, both
-# services will be enabled.
-
-# The following services are disabled
-sentinel['enable'] = false
-bootstrap['enable'] = false
-nginx['enable'] = false
-postgresql['enable'] = false
-gitlab_rails['enable'] = false
-mailroom['enable'] = false
-
-# For Redis Replica role, also change this setting from default 'true' to 'false':
-redis['master'] = false
-```
-
-You can find the relevant attributes defined in [`gitlab_rails.rb`](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb).
-
-## Troubleshooting
-
-There are a lot of moving parts that needs to be taken care carefully
-in order for the HA setup to work as expected.
-
-Before proceeding with the troubleshooting below, check your firewall rules:
-
-- Redis machines
- - Accept TCP connection in `6379`
- - Connect to the other Redis machines via TCP in `6379`
-- Sentinel machines
- - Accept TCP connection in `26379`
- - Connect to other Sentinel machines via TCP in `26379`
- - Connect to the Redis machines via TCP in `6379`
-
-### Troubleshooting Redis replication
-
-You can check if everything is correct by connecting to each server using
-`redis-cli` application, and sending the `info replication` command as below.
-
-```shell
-/opt/gitlab/embedded/bin/redis-cli -h <redis-host-or-ip> -a '<redis-password>' info replication
-```
-
-When connected to a `master` Redis, you will see the number of connected
-`replicas`, and a list of each with connection details:
-
-```plaintext
-# Replication
-role:master
-connected_replicas:1
-replica0:ip=10.133.5.21,port=6379,state=online,offset=208037514,lag=1
-master_repl_offset:208037658
-repl_backlog_active:1
-repl_backlog_size:1048576
-repl_backlog_first_byte_offset:206989083
-repl_backlog_histlen:1048576
-```
-
-When it's a `replica`, you will see details of the master connection and if
-its `up` or `down`:
-
-```plaintext
-# Replication
-role:replica
-master_host:10.133.1.58
-master_port:6379
-master_link_status:up
-master_last_io_seconds_ago:1
-master_sync_in_progress:0
-replica_repl_offset:208096498
-replica_priority:100
-replica_read_only:1
-connected_replicas:0
-master_repl_offset:0
-repl_backlog_active:0
-repl_backlog_size:1048576
-repl_backlog_first_byte_offset:0
-repl_backlog_histlen:0
-```
-
-### Troubleshooting Sentinel
-
-If you get an error like: `Redis::CannotConnectError: No sentinels available.`,
-there may be something wrong with your configuration files or it can be related
-to [this issue](https://github.com/redis/redis-rb/issues/531).
-
-You must make sure you are defining the same value in `redis['master_name']`
-and `redis['master_pasword']` as you defined for your sentinel node.
-
-The way the Redis connector `redis-rb` works with sentinel is a bit
-non-intuitive. We try to hide the complexity in omnibus, but it still requires
-a few extra configurations.
-
----
-
-To make sure your configuration is correct:
-
-1. SSH into your GitLab application server
-1. Enter the Rails console:
-
- ```shell
- # For Omnibus installations
- sudo gitlab-rails console
-
- # For source installations
- sudo -u git rails console -e production
- ```
-
-1. Run in the console:
-
- ```ruby
- redis = Redis.new(Gitlab::Redis::SharedState.params)
- redis.info
- ```
-
- Keep this screen open and try to simulate a failover below.
-
-1. To simulate a failover on master Redis, SSH into the Redis server and run:
-
- ```shell
- # port must match your master redis port, and the sleep time must be a few seconds bigger than defined one
- redis-cli -h localhost -p 6379 DEBUG sleep 20
- ```
-
-1. Then back in the Rails console from the first step, run:
-
- ```ruby
- redis.info
- ```
-
- You should see a different port after a few seconds delay
- (the failover/reconnect time).
-
-## Changelog
-
-Changes to Redis HA over time.
-
-**8.14**
-
-- Redis Sentinel support is production-ready and bundled in the Omnibus GitLab
- Enterprise Edition package
-- Documentation restructure for better readability
-
-**8.11**
-
-- Experimental Redis Sentinel support was added
-
-## Further reading
-
-Read more on High Availability:
-
-1. [High Availability Overview](README.md)
-1. [Configure the database](../postgresql/replication_and_failover.md)
-1. [Configure NFS](nfs.md)
-1. [Configure the GitLab application servers](gitlab.md)
-1. [Configure the load balancers](load_balancer.md)
+This document was moved to [another location](../redis/index.md).
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 97be480bc3b..75496638979 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -1,371 +1,5 @@
---
-type: reference
+redirect_to: ../redis/replication_and_failover_external.md
---
-# Configuring non-Omnibus Redis for GitLab HA
-
-This is the documentation for configuring a Highly Available Redis setup when
-you have installed Redis all by yourself and not using the bundled one that
-comes with the Omnibus packages.
-
-Note also that you may elect to override all references to
-`/home/git/gitlab/config/resque.yml` in accordance with the advanced Redis
-settings outlined in
-[Configuration Files Documentation](https://gitlab.com/gitlab-org/gitlab/blob/master/config/README.md).
-
-We cannot stress enough the importance of reading the
-[Overview section](redis.md#overview) of the Omnibus Redis HA as it provides
-some invaluable information to the configuration of Redis. Please proceed to
-read it before going forward with this guide.
-
-We also highly recommend that you use the Omnibus GitLab packages, as we
-optimize them specifically for GitLab, and we will take care of upgrading Redis
-to the latest supported version.
-
-If you're not sure whether this guide is for you, please refer to
-[Available configuration setups](redis.md#available-configuration-setups) in
-the Omnibus Redis HA documentation.
-
-## Configuring your own Redis server
-
-This is the section where we install and set up the new Redis instances.
-
-### Prerequisites
-
-- All Redis servers in this guide must be configured to use a TCP connection
- instead of a socket. To configure Redis to use TCP connections you need to
- define both `bind` and `port` in the Redis config file. You can bind to all
- interfaces (`0.0.0.0`) or specify the IP of the desired interface
- (e.g., one from an internal network).
-- Since Redis 3.2, you must define a password to receive external connections
- (`requirepass`).
-- If you are using Redis with Sentinel, you will also need to define the same
- password for the replica password definition (`masterauth`) in the same instance.
-
-In addition, read the prerequisites as described in the
-[Omnibus Redis HA document](redis.md#prerequisites) since they provide some
-valuable information for the general setup.
-
-### Step 1. Configuring the master Redis instance
-
-Assuming that the Redis master instance IP is `10.0.0.1`:
-
-1. [Install Redis](../../install/installation.md#7-redis).
-1. Edit `/etc/redis/redis.conf`:
-
- ```conf
- ## Define a `bind` address pointing to a local IP that your other machines
- ## can reach you. If you really need to bind to an external accessible IP, make
- ## sure you add extra firewall rules to prevent unauthorized access:
- bind 10.0.0.1
-
- ## Define a `port` to force redis to listen on TCP so other machines can
- ## connect to it (default port is `6379`).
- port 6379
-
- ## Set up password authentication (use the same password in all nodes).
- ## The password should be defined equal for both `requirepass` and `masterauth`
- ## when setting up Redis to use with Sentinel.
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
- ```
-
-1. Restart the Redis service for the changes to take effect.
-
-### Step 2. Configuring the replica Redis instances
-
-Assuming that the Redis replica instance IP is `10.0.0.2`:
-
-1. [Install Redis](../../install/installation.md#7-redis).
-1. Edit `/etc/redis/redis.conf`:
-
- ```conf
- ## Define a `bind` address pointing to a local IP that your other machines
- ## can reach you. If you really need to bind to an external accessible IP, make
- ## sure you add extra firewall rules to prevent unauthorized access:
- bind 10.0.0.2
-
- ## Define a `port` to force redis to listen on TCP so other machines can
- ## connect to it (default port is `6379`).
- port 6379
-
- ## Set up password authentication (use the same password in all nodes).
- ## The password should be defined equal for both `requirepass` and `masterauth`
- ## when setting up Redis to use with Sentinel.
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
-
- ## Define `replicaof` pointing to the Redis master instance with IP and port.
- replicaof 10.0.0.1 6379
- ```
-
-1. Restart the Redis service for the changes to take effect.
-1. Go through the steps again for all the other replica nodes.
-
-### Step 3. Configuring the Redis Sentinel instances
-
-Sentinel is a special type of Redis server. It inherits most of the basic
-configuration options you can define in `redis.conf`, with specific ones
-starting with `sentinel` prefix.
-
-Assuming that the Redis Sentinel is installed on the same instance as Redis
-master with IP `10.0.0.1` (some settings might overlap with the master):
-
-1. [Install Redis Sentinel](https://redis.io/topics/sentinel)
-1. Edit `/etc/redis/sentinel.conf`:
-
- ```conf
- ## Define a `bind` address pointing to a local IP that your other machines
- ## can reach you. If you really need to bind to an external accessible IP, make
- ## sure you add extra firewall rules to prevent unauthorized access:
- bind 10.0.0.1
-
- ## Define a `port` to force Sentinel to listen on TCP so other machines can
- ## connect to it (default port is `6379`).
- port 26379
-
- ## Set up password authentication (use the same password in all nodes).
- ## The password should be defined equal for both `requirepass` and `masterauth`
- ## when setting up Redis to use with Sentinel.
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
-
- ## Define with `sentinel auth-pass` the same shared password you have
- ## defined for both Redis master and replicas instances.
- sentinel auth-pass gitlab-redis redis-password-goes-here
-
- ## Define with `sentinel monitor` the IP and port of the Redis
- ## master node, and the quorum required to start a failover.
- sentinel monitor gitlab-redis 10.0.0.1 6379 2
-
- ## Define with `sentinel down-after-milliseconds` the time in `ms`
- ## that an unresponsive server will be considered down.
- sentinel down-after-milliseconds gitlab-redis 10000
-
- ## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
- ## meanings:
- ##
- ## * The time needed to re-start a failover after a previous failover was
- ## already tried against the same master by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## * The time needed for a replica replicating to a wrong master according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right master, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## * The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## * The maximum time a failover in progress waits for all the replicas to be
- ## reconfigured as replicas of the new master. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- sentinel failover_timeout 30000
- ```
-
-1. Restart the Redis service for the changes to take effect.
-1. Go through the steps again for all the other Sentinel nodes.
-
-### Step 4. Configuring the GitLab application
-
-You can enable or disable Sentinel support at any time in new or existing
-installations. From the GitLab application perspective, all it requires is
-the correct credentials for the Sentinel nodes.
-
-While it doesn't require a list of all Sentinel nodes, in case of a failure,
-it needs to access at least one of listed ones.
-
-The following steps should be performed in the [GitLab application server](gitlab.md)
-which ideally should not have Redis or Sentinels in the same machine for a HA
-setup:
-
-1. Edit `/home/git/gitlab/config/resque.yml` following the example in
- [resque.yml.example](https://gitlab.com/gitlab-org/gitlab/blob/master/config/resque.yml.example), and uncomment the Sentinel lines, pointing to
- the correct server credentials:
-
- ```yaml
- # resque.yaml
- production:
- url: redis://:redi-password-goes-here@gitlab-redis/
- sentinels:
- -
- host: 10.0.0.1
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.2
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.3
- port: 26379 # point to sentinel, not to redis port
- ```
-
-1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
-
-## Example of minimal configuration with 1 master, 2 replicas and 3 Sentinels
-
-In this example we consider that all servers have an internal network
-interface with IPs in the `10.0.0.x` range, and that they can connect
-to each other using these IPs.
-
-In a real world usage, you would also set up firewall rules to prevent
-unauthorized access from other machines, and block traffic from the
-outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
-
-For this example, **Sentinel 1** will be configured in the same machine as the
-**Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the
-**Replica 1** and **Replica 2** respectively.
-
-Here is a list and description of each **machine** and the assigned **IP**:
-
-- `10.0.0.1`: Redis Master + Sentinel 1
-- `10.0.0.2`: Redis Replica 1 + Sentinel 2
-- `10.0.0.3`: Redis Replica 2 + Sentinel 3
-- `10.0.0.4`: GitLab application
-
-Please note that after the initial configuration, if a failover is initiated
-by the Sentinel nodes, the Redis nodes will be reconfigured and the **Master**
-will change permanently (including in `redis.conf`) from one node to the other,
-until a new failover is initiated again.
-
-The same thing will happen with `sentinel.conf` that will be overridden after the
-initial execution, after any new sentinel node starts watching the **Master**,
-or a failover promotes a different **Master** node.
-
-### Example configuration for Redis master and Sentinel 1
-
-1. In `/etc/redis/redis.conf`:
-
- ```conf
- bind 10.0.0.1
- port 6379
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
- ```
-
-1. In `/etc/redis/sentinel.conf`:
-
- ```conf
- bind 10.0.0.1
- port 26379
- sentinel auth-pass gitlab-redis redis-password-goes-here
- sentinel monitor gitlab-redis 10.0.0.1 6379 2
- sentinel down-after-milliseconds gitlab-redis 10000
- sentinel failover_timeout 30000
- ```
-
-1. Restart the Redis service for the changes to take effect.
-
-### Example configuration for Redis replica 1 and Sentinel 2
-
-1. In `/etc/redis/redis.conf`:
-
- ```conf
- bind 10.0.0.2
- port 6379
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
- replicaof 10.0.0.1 6379
- ```
-
-1. In `/etc/redis/sentinel.conf`:
-
- ```conf
- bind 10.0.0.2
- port 26379
- sentinel auth-pass gitlab-redis redis-password-goes-here
- sentinel monitor gitlab-redis 10.0.0.1 6379 2
- sentinel down-after-milliseconds gitlab-redis 10000
- sentinel failover_timeout 30000
- ```
-
-1. Restart the Redis service for the changes to take effect.
-
-### Example configuration for Redis replica 2 and Sentinel 3
-
-1. In `/etc/redis/redis.conf`:
-
- ```conf
- bind 10.0.0.3
- port 6379
- requirepass redis-password-goes-here
- masterauth redis-password-goes-here
- replicaof 10.0.0.1 6379
- ```
-
-1. In `/etc/redis/sentinel.conf`:
-
- ```conf
- bind 10.0.0.3
- port 26379
- sentinel auth-pass gitlab-redis redis-password-goes-here
- sentinel monitor gitlab-redis 10.0.0.1 6379 2
- sentinel down-after-milliseconds gitlab-redis 10000
- sentinel failover_timeout 30000
- ```
-
-1. Restart the Redis service for the changes to take effect.
-
-### Example configuration of the GitLab application
-
-1. Edit `/home/git/gitlab/config/resque.yml`:
-
- ```yaml
- production:
- url: redis://:redi-password-goes-here@gitlab-redis/
- sentinels:
- -
- host: 10.0.0.1
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.2
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.3
- port: 26379 # point to sentinel, not to redis port
- ```
-
-1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
-
-## Troubleshooting
-
-We have a more detailed [Troubleshooting](redis.md#troubleshooting) explained
-in the documentation for Omnibus GitLab installations. Here we will list only
-the things that are specific to a source installation.
-
-If you get an error in GitLab like `Redis::CannotConnectError: No sentinels available.`,
-there may be something wrong with your configuration files or it can be related
-to [this upstream issue](https://github.com/redis/redis-rb/issues/531).
-
-You must make sure that `resque.yml` and `sentinel.conf` are configured correctly,
-otherwise `redis-rb` will not work properly.
-
-The `master-group-name` (`gitlab-redis`) defined in (`sentinel.conf`)
-**must** be used as the hostname in GitLab (`resque.yml`):
-
-```conf
-# sentinel.conf:
-sentinel monitor gitlab-redis 10.0.0.1 6379 2
-sentinel down-after-milliseconds gitlab-redis 10000
-sentinel config-epoch gitlab-redis 0
-sentinel leader-epoch gitlab-redis 0
-```
-
-```yaml
-# resque.yaml
-production:
- url: redis://:myredispassword@gitlab-redis/
- sentinels:
- -
- host: 10.0.0.1
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.2
- port: 26379 # point to sentinel, not to redis port
- -
- host: 10.0.0.3
- port: 26379 # point to sentinel, not to redis port
-```
-
-When in doubt, please read [Redis Sentinel documentation](https://redis.io/topics/sentinel).
+This document was moved to [another location](../redis/replication_and_failover_external.md).
diff --git a/doc/administration/redis/index.md b/doc/administration/redis/index.md
new file mode 100644
index 00000000000..0bd56666ab8
--- /dev/null
+++ b/doc/administration/redis/index.md
@@ -0,0 +1,42 @@
+---
+type: index
+stage: Enablement
+group: Distribution
+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/#designated-technical-writers
+---
+
+# Configuring Redis for scaling
+
+Based on your infrastructure setup and how you have installed GitLab, there are
+multiple ways to configure Redis.
+
+You can choose to install and manage Redis and Sentinel yourself, use a hosted
+cloud solution, or you can use the ones that come bundled with the Omnibus GitLab
+packages so you only need to focus on configuration. Pick the one that suits your needs.
+
+## Redis replication and failover using Omnibus GitLab
+
+This setup is for when you have installed GitLab using the
+[Omnibus GitLab **Enterprise Edition** (EE) package](https://about.gitlab.com/install/?version=ee).
+
+Both Redis and Sentinel are bundled in the package, so you can it to set up the whole
+Redis infrastructure (primary, replica and sentinel).
+
+[> Read how to set up Redis replication and failover using Omnibus GitLab](replication_and_failover.md)
+
+## Redis replication and failover using the non-bundled Redis
+
+This setup is for when you have installed GitLab using the
+[Omnibus GitLab packages](https://about.gitlab.com/install/) (CE or EE),
+or installed it [from source](../../install/installation.md), but you want to use
+your own external Redis and sentinel servers.
+
+[> Read how to set up Redis replication and failover using the non-bundled Redis](replication_and_failover_external.md)
+
+## Standalone Redis using Omnibus GitLab
+
+This setup is for when you have installed the
+[Omnibus GitLab **Community Edition** (CE) package](https://about.gitlab.com/install/?version=ce)
+to use the bundled Redis, so you can use the package with only the Redis service enabled.
+
+[> Read how to set up a standalone Redis instance using Omnibus GitLab](standalone.md)
diff --git a/doc/administration/redis/replication_and_failover.md b/doc/administration/redis/replication_and_failover.md
new file mode 100644
index 00000000000..d95320b6669
--- /dev/null
+++ b/doc/administration/redis/replication_and_failover.md
@@ -0,0 +1,740 @@
+---
+type: howto
+stage: Enablement
+group: Distribution
+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/#designated-technical-writers
+---
+
+# Redis replication and failover with Omnibus GitLab **(PREMIUM ONLY)**
+
+NOTE: **Note:**
+This is the documentation for the Omnibus GitLab packages. For using your own
+non-bundled Redis, follow the [relevant documentation](replication_and_failover_external.md).
+
+NOTE: **Note:**
+In Redis lingo, primary is called master. In this document, primary is used
+instead of master, except the settings where `master` is required.
+
+Using [Redis](https://redis.io/) in scalable environment is possible using a **Primary** x **Replica**
+topology with a [Redis Sentinel](https://redis.io/topics/sentinel) service to watch and automatically
+start the failover procedure.
+
+Redis requires authentication if used with Sentinel. See
+[Redis Security](https://redis.io/topics/security) documentation for more
+information. We recommend using a combination of a Redis password and tight
+firewall rules to secure your Redis service.
+You are highly encouraged to read the [Redis Sentinel](https://redis.io/topics/sentinel) documentation
+before configuring Redis with GitLab to fully understand the topology and
+architecture.
+
+Before diving into the details of setting up Redis and Redis Sentinel for a
+replicated topology, make sure you read this document once as a whole to better
+understand how the components are tied together.
+
+You need at least `3` independent machines: physical, or VMs running into
+distinct physical machines. It is essential that all primary and replica Redis
+instances run in different machines. If you fail to provision the machines in
+that specific way, any issue with the shared environment can bring your entire
+setup down.
+
+It is OK to run a Sentinel alongside of a primary or replica Redis instance.
+There should be no more than one Sentinel on the same machine though.
+
+You also need to take into consideration the underlying network topology,
+making sure you have redundant connectivity between Redis / Sentinel and
+GitLab instances, otherwise the networks will become a single point of
+failure.
+
+Running Redis in a scaled environment requires a few things:
+
+- Multiple Redis instances
+- Run Redis in a **Primary** x **Replica** topology
+- Multiple Sentinel instances
+- Application support and visibility to all Sentinel and Redis instances
+
+Redis Sentinel can handle the most important tasks in an HA environment and that's
+to help keep servers online with minimal to no downtime. Redis Sentinel:
+
+- Monitors **Primary** and **Replicas** instances to see if they are available
+- Promotes a **Replica** to **Primary** when the **Primary** fails
+- Demotes a **Primary** to **Replica** when the failed **Primary** comes back online
+ (to prevent data-partitioning)
+- Can be queried by the application to always connect to the current **Primary**
+ server
+
+When a **Primary** fails to respond, it's the application's responsibility
+(in our case GitLab) to handle timeout and reconnect (querying a **Sentinel**
+for a new **Primary**).
+
+To get a better understanding on how to correctly set up Sentinel, please read
+the [Redis Sentinel documentation](https://redis.io/topics/sentinel) first, as
+failing to configure it correctly can lead to data loss or can bring your
+whole cluster down, invalidating the failover effort.
+
+## Recommended setup
+
+For a minimal setup, you will install the Omnibus GitLab package in `3`
+**independent** machines, both with **Redis** and **Sentinel**:
+
+- Redis Primary + Sentinel
+- Redis Replica + Sentinel
+- Redis Replica + Sentinel
+
+If you are not sure or don't understand why and where the amount of nodes come
+from, read [Redis setup overview](#redis-setup-overview) and
+[Sentinel setup overview](#sentinel-setup-overview).
+
+For a recommended setup that can resist more failures, you will install
+the Omnibus GitLab package in `5` **independent** machines, both with
+**Redis** and **Sentinel**:
+
+- Redis Primary + Sentinel
+- Redis Replica + Sentinel
+- Redis Replica + Sentinel
+- Redis Replica + Sentinel
+- Redis Replica + Sentinel
+
+### Redis setup overview
+
+You must have at least `3` Redis servers: `1` primary, `2` Replicas, and they
+need to each be on independent machines (see explanation above).
+
+You can have additional Redis nodes, that will help survive a situation
+where more nodes goes down. Whenever there is only `2` nodes online, a failover
+will not be initiated.
+
+As an example, if you have `6` Redis nodes, a maximum of `3` can be
+simultaneously down.
+
+Please note that there are different requirements for Sentinel nodes.
+If you host them in the same Redis machines, you may need to take
+that restrictions into consideration when calculating the amount of
+nodes to be provisioned. See [Sentinel setup overview](#sentinel-setup-overview)
+documentation for more information.
+
+All Redis nodes should be configured the same way and with similar server specs, as
+in a failover situation, any **Replica** can be promoted as the new **Primary** by
+the Sentinel servers.
+
+The replication requires authentication, so you need to define a password to
+protect all Redis nodes and the Sentinels. They will all share the same
+password, and all instances must be able to talk to
+each other over the network.
+
+### Sentinel setup overview
+
+Sentinels watch both other Sentinels and Redis nodes. Whenever a Sentinel
+detects that a Redis node is not responding, it will announce that to the
+other Sentinels. They have to reach the **quorum**, that is the minimum amount
+of Sentinels that agrees a node is down, in order to be able to start a failover.
+
+Whenever the **quorum** is met, the **majority** of all known Sentinel nodes
+need to be available and reachable, so that they can elect the Sentinel **leader**
+who will take all the decisions to restore the service availability by:
+
+- Promoting a new **Primary**
+- Reconfiguring the other **Replicas** and make them point to the new **Primary**
+- Announce the new **Primary** to every other Sentinel peer
+- Reconfigure the old **Primary** and demote to **Replica** when it comes back online
+
+You must have at least `3` Redis Sentinel servers, and they need to
+be each in an independent machine (that are believed to fail independently),
+ideally in different geographical areas.
+
+You can configure them in the same machines where you've configured the other
+Redis servers, but understand that if a whole node goes down, you loose both
+a Sentinel and a Redis instance.
+
+The number of sentinels should ideally always be an **odd** number, for the
+consensus algorithm to be effective in the case of a failure.
+
+In a `3` nodes topology, you can only afford `1` Sentinel node going down.
+Whenever the **majority** of the Sentinels goes down, the network partition
+protection prevents destructive actions and a failover **will not be started**.
+
+Here are some examples:
+
+- With `5` or `6` sentinels, a maximum of `2` can go down for a failover begin.
+- With `7` sentinels, a maximum of `3` nodes can go down.
+
+The **Leader** election can sometimes fail the voting round when **consensus**
+is not achieved (see the odd number of nodes requirement above). In that case,
+a new attempt will be made after the amount of time defined in
+`sentinel['failover_timeout']` (in milliseconds).
+
+NOTE: **Note:**
+We will see where `sentinel['failover_timeout']` is defined later.
+
+The `failover_timeout` variable has a lot of different use cases. According to
+the official documentation:
+
+- The time needed to re-start a failover after a previous failover was
+ already tried against the same primary by a given Sentinel, is two
+ times the failover timeout.
+
+- The time needed for a replica replicating to a wrong primary according
+ to a Sentinel current configuration, to be forced to replicate
+ with the right primary, is exactly the failover timeout (counting since
+ the moment a Sentinel detected the misconfiguration).
+
+- The time needed to cancel a failover that is already in progress but
+ did not produced any configuration change (REPLICAOF NO ONE yet not
+ acknowledged by the promoted replica).
+
+- The maximum time a failover in progress waits for all the replicas to be
+ reconfigured as replicas of the new primary. However even after this time
+ the replicas will be reconfigured by the Sentinels anyway, but not with
+ the exact parallel-syncs progression as specified.
+
+## Configuring Redis
+
+This is the section where we install and set up the new Redis instances.
+
+It is assumed that you have installed GitLab and all its components from scratch.
+If you already have Redis installed and running, read how to
+[switch from a single-machine installation](#switching-from-an-existing-single-machine-installation).
+
+NOTE: **Note:**
+Redis nodes (both primary and replica) will need the same password defined in
+`redis['password']`. At any time during a failover the Sentinels can
+reconfigure a node and change its status from primary to replica and vice versa.
+
+### Requirements
+
+The requirements for a Redis setup are the following:
+
+1. Provision the minimum required number of instances as specified in the
+ [recommended setup](#recommended-setup) section.
+1. We **Do not** recommend installing Redis or Redis Sentinel in the same machines your
+ GitLab application is running on as this weakens your HA configuration. You can however opt in to install Redis
+ and Sentinel in the same machine.
+1. All Redis nodes must be able to talk to each other and accept incoming
+ connections over Redis (`6379`) and Sentinel (`26379`) ports (unless you
+ change the default ones).
+1. The server that hosts the GitLab application must be able to access the
+ Redis nodes.
+1. Protect the nodes from access from external networks ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)), using
+ firewall.
+
+### Switching from an existing single-machine installation
+
+If you already have a single-machine GitLab install running, you will need to
+replicate from this machine first, before de-activating the Redis instance
+inside it.
+
+Your single-machine install will be the initial **Primary**, and the `3` others
+should be configured as **Replica** pointing to this machine.
+
+After replication catches up, you will need to stop services in the
+single-machine install, to rotate the **Primary** to one of the new nodes.
+
+Make the required changes in configuration and restart the new nodes again.
+
+To disable Redis in the single install, edit `/etc/gitlab/gitlab.rb`:
+
+```ruby
+redis['enable'] = false
+```
+
+If you fail to replicate first, you may loose data (unprocessed background jobs).
+
+### Step 1. Configuring the primary Redis instance
+
+1. SSH into the **Primary** Redis server.
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
+ package you want using **steps 1 and 2** from the GitLab downloads page.
+ - Make sure you select the correct Omnibus package, with the same version
+ and type (Community, Enterprise editions) of your current install.
+ - Do not complete any other steps on the download page.
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+
+ ```ruby
+ # Specify server role as 'redis_master_role'
+ roles ['redis_master_role']
+
+ # IP address pointing to a local IP that the other machines can reach to.
+ # You can also set bind to '0.0.0.0' which listen in all interfaces.
+ # If you really need to bind to an external accessible IP, make
+ # sure you add extra firewall rules to prevent unauthorized access.
+ redis['bind'] = '10.0.0.1'
+
+ # Define a port so Redis can listen for TCP requests which will allow other
+ # machines to connect to it.
+ redis['port'] = 6379
+
+ # Set up password authentication for Redis (use the same password in all nodes).
+ redis['password'] = 'redis-password-goes-here'
+ ```
+
+1. Only the primary GitLab application server should handle migrations. To
+ prevent database migrations from running on upgrade, add the following
+ configuration to your `/etc/gitlab/gitlab.rb` file:
+
+ ```ruby
+ gitlab_rails['auto_migrate'] = false
+ ```
+
+1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+NOTE: **Note:**
+You can specify multiple roles like sentinel and Redis as:
+`roles ['redis_sentinel_role', 'redis_master_role']`.
+Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
+
+### Step 2. Configuring the replica Redis instances
+
+1. SSH into the **replica** Redis server.
+1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
+ package you want using **steps 1 and 2** from the GitLab downloads page.
+ - Make sure you select the correct Omnibus package, with the same version
+ and type (Community, Enterprise editions) of your current install.
+ - Do not complete any other steps on the download page.
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+
+ ```ruby
+ # Specify server role as 'redis_replica_role'
+ roles ['redis_replica_role']
+
+ # IP address pointing to a local IP that the other machines can reach to.
+ # You can also set bind to '0.0.0.0' which listen in all interfaces.
+ # If you really need to bind to an external accessible IP, make
+ # sure you add extra firewall rules to prevent unauthorized access.
+ redis['bind'] = '10.0.0.2'
+
+ # Define a port so Redis can listen for TCP requests which will allow other
+ # machines to connect to it.
+ redis['port'] = 6379
+
+ # The same password for Redis authentication you set up for the primary node.
+ redis['password'] = 'redis-password-goes-here'
+
+ # The IP of the primary Redis node.
+ redis['master_ip'] = '10.0.0.1'
+
+ # Port of primary Redis server, uncomment to change to non default. Defaults
+ # to `6379`.
+ #redis['master_port'] = 6379
+ ```
+
+1. To prevent reconfigure from running automatically on upgrade, run:
+
+ ```shell
+ sudo touch /etc/gitlab/skip-auto-reconfigure
+ ```
+
+1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Go through the steps again for all the other replica nodes.
+
+NOTE: **Note:**
+You can specify multiple roles like sentinel and Redis as:
+`roles ['redis_sentinel_role', 'redis_master_role']`.
+Read more about [roles](https://docs.gitlab.com/omnibus/roles/).
+
+These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
+a failover, as the nodes will be managed by the Sentinels, and even after a
+`gitlab-ctl reconfigure`, they will get their configuration restored by
+the same Sentinels.
+
+### Step 3. Configuring the Redis Sentinel instances
+
+NOTE: **Note:** If you are using an external Redis Sentinel instance, be sure
+to exclude the `requirepass` parameter from the Sentinel
+configuration. This parameter will cause clients to report `NOAUTH
+Authentication required.`. [Redis Sentinel 3.2.x does not support
+password authentication](https://github.com/antirez/redis/issues/3279).
+
+Now that the Redis servers are all set up, let's configure the Sentinel
+servers.
+
+If you are not sure if your Redis servers are working and replicating
+correctly, please read the [Troubleshooting Replication](troubleshooting.md#troubleshooting-redis-replication)
+and fix it before proceeding with Sentinel setup.
+
+You must have at least `3` Redis Sentinel servers, and they need to
+be each in an independent machine. You can configure them in the same
+machines where you've configured the other Redis servers.
+
+With GitLab Enterprise Edition, you can use the Omnibus package to set up
+multiple machines with the Sentinel daemon.
+
+---
+
+1. SSH into the server that will host Redis Sentinel.
+1. **You can omit this step if the Sentinels will be hosted in the same node as
+ the other Redis instances.**
+
+ [Download/install](https://about.gitlab.com/install/) the
+ Omnibus GitLab Enterprise Edition package using **steps 1 and 2** from the
+ GitLab downloads page.
+ - Make sure you select the correct Omnibus package, with the same version
+ the GitLab application is running.
+ - Do not complete any other steps on the download page.
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the contents (if you are installing the
+ Sentinels in the same node as the other Redis instances, some values might
+ be duplicate below):
+
+ ```ruby
+ roles ['redis_sentinel_role']
+
+ # Must be the same in every sentinel node
+ redis['master_name'] = 'gitlab-redis'
+
+ # The same password for Redis authentication you set up for the primary node.
+ redis['master_password'] = 'redis-password-goes-here'
+
+ # The IP of the primary Redis node.
+ redis['master_ip'] = '10.0.0.1'
+
+ # Define a port so Redis can listen for TCP requests which will allow other
+ # machines to connect to it.
+ redis['port'] = 6379
+
+ # Port of primary Redis server, uncomment to change to non default. Defaults
+ # to `6379`.
+ #redis['master_port'] = 6379
+
+ ## Configure Sentinel
+ sentinel['bind'] = '10.0.0.1'
+
+ # Port that Sentinel listens on, uncomment to change to non default. Defaults
+ # to `26379`.
+ # sentinel['port'] = 26379
+
+ ## Quorum must reflect the amount of voting sentinels it take to start a failover.
+ ## Value must NOT be greater then the amount of sentinels.
+ ##
+ ## The quorum can be used to tune Sentinel in two ways:
+ ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
+ ## we deploy, we are basically making Sentinel more sensible to primary failures,
+ ## triggering a failover as soon as even just a minority of Sentinels is no longer
+ ## able to talk with the primary.
+ ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
+ ## making Sentinel able to failover only when there are a very large number (larger
+ ## than majority) of well connected Sentinels which agree about the primary being down.s
+ sentinel['quorum'] = 2
+
+ ## Consider unresponsive server down after x amount of ms.
+ # sentinel['down_after_milliseconds'] = 10000
+
+ ## Specifies the failover timeout in milliseconds. It is used in many ways:
+ ##
+ ## - The time needed to re-start a failover after a previous failover was
+ ## already tried against the same primary by a given Sentinel, is two
+ ## times the failover timeout.
+ ##
+ ## - The time needed for a replica replicating to a wrong primary according
+ ## to a Sentinel current configuration, to be forced to replicate
+ ## with the right primary, is exactly the failover timeout (counting since
+ ## the moment a Sentinel detected the misconfiguration).
+ ##
+ ## - The time needed to cancel a failover that is already in progress but
+ ## did not produced any configuration change (REPLICAOF NO ONE yet not
+ ## acknowledged by the promoted replica).
+ ##
+ ## - The maximum time a failover in progress waits for all the replica to be
+ ## reconfigured as replicas of the new primary. However even after this time
+ ## the replicas will be reconfigured by the Sentinels anyway, but not with
+ ## the exact parallel-syncs progression as specified.
+ # sentinel['failover_timeout'] = 60000
+ ```
+
+1. To prevent database migrations from running on upgrade, run:
+
+ ```shell
+ sudo touch /etc/gitlab/skip-auto-reconfigure
+ ```
+
+ Only the primary GitLab application server should handle migrations.
+
+1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Go through the steps again for all the other Sentinel nodes.
+
+### Step 4. Configuring the GitLab application
+
+The final part is to inform the main GitLab application server of the Redis
+Sentinels servers and authentication credentials.
+
+You can enable or disable Sentinel support at any time in new or existing
+installations. From the GitLab application perspective, all it requires is
+the correct credentials for the Sentinel nodes.
+
+While it doesn't require a list of all Sentinel nodes, in case of a failure,
+it needs to access at least one of the listed.
+
+NOTE: **Note:**
+The following steps should be performed in the [GitLab application server](../high_availability/gitlab.md)
+which ideally should not have Redis or Sentinels on it for a HA setup.
+
+1. SSH into the server where the GitLab application is installed.
+1. Edit `/etc/gitlab/gitlab.rb` and add/change the following lines:
+
+ ```ruby
+ ## Must be the same in every sentinel node
+ redis['master_name'] = 'gitlab-redis'
+
+ ## The same password for Redis authentication you set up for the primary node.
+ redis['master_password'] = 'redis-password-goes-here'
+
+ ## A list of sentinels with `host` and `port`
+ gitlab_rails['redis_sentinels'] = [
+ {'host' => '10.0.0.1', 'port' => 26379},
+ {'host' => '10.0.0.2', 'port' => 26379},
+ {'host' => '10.0.0.3', 'port' => 26379}
+ ]
+ ```
+
+1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+### Step 5. Enable Monitoring
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3786) in GitLab 12.0.
+
+If you enable Monitoring, it must be enabled on **all** Redis servers.
+
+1. Make sure to collect [`CONSUL_SERVER_NODES`](../postgresql/replication_and_failover.md#consul-information), which are the IP addresses or DNS records of the Consul server nodes, for the next step. Note they are presented as `Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z`
+
+1. Create/edit `/etc/gitlab/gitlab.rb` and add the following configuration:
+
+ ```ruby
+ # Enable service discovery for Prometheus
+ consul['enable'] = true
+ consul['monitoring_service_discovery'] = true
+
+ # Replace placeholders
+ # Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z
+ # with the addresses of the Consul server nodes
+ consul['configuration'] = {
+ retry_join: %w(Y.Y.Y.Y consul1.gitlab.example.com Z.Z.Z.Z),
+ }
+
+ # Set the network addresses that the exporters will listen on
+ node_exporter['listen_address'] = '0.0.0.0:9100'
+ redis_exporter['listen_address'] = '0.0.0.0:9121'
+ ```
+
+1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
+
+## Example of a minimal configuration with 1 primary, 2 replicas and 3 Sentinels
+
+In this example we consider that all servers have an internal network
+interface with IPs in the `10.0.0.x` range, and that they can connect
+to each other using these IPs.
+
+In a real world usage, you would also set up firewall rules to prevent
+unauthorized access from other machines and block traffic from the
+outside (Internet).
+
+We will use the same `3` nodes with **Redis** + **Sentinel** topology
+discussed in [Redis setup overview](#redis-setup-overview) and
+[Sentinel setup overview](#sentinel-setup-overview) documentation.
+
+Here is a list and description of each **machine** and the assigned **IP**:
+
+- `10.0.0.1`: Redis primary + Sentinel 1
+- `10.0.0.2`: Redis Replica 1 + Sentinel 2
+- `10.0.0.3`: Redis Replica 2 + Sentinel 3
+- `10.0.0.4`: GitLab application
+
+Please note that after the initial configuration, if a failover is initiated
+by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
+will change permanently (including in `redis.conf`) from one node to the other,
+until a new failover is initiated again.
+
+The same thing will happen with `sentinel.conf` that will be overridden after the
+initial execution, after any new sentinel node starts watching the **Primary**,
+or a failover promotes a different **Primary** node.
+
+### Example configuration for Redis primary and Sentinel 1
+
+In `/etc/gitlab/gitlab.rb`:
+
+```ruby
+roles ['redis_sentinel_role', 'redis_master_role']
+redis['bind'] = '10.0.0.1'
+redis['port'] = 6379
+redis['password'] = 'redis-password-goes-here'
+redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
+redis['master_password'] = 'redis-password-goes-here' # the same value defined in redis['password'] in the primary instance
+redis['master_ip'] = '10.0.0.1' # ip of the initial primary redis instance
+#redis['master_port'] = 6379 # port of the initial primary redis instance, uncomment to change to non default
+sentinel['bind'] = '10.0.0.1'
+# sentinel['port'] = 26379 # uncomment to change default port
+sentinel['quorum'] = 2
+# sentinel['down_after_milliseconds'] = 10000
+# sentinel['failover_timeout'] = 60000
+```
+
+[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+### Example configuration for Redis replica 1 and Sentinel 2
+
+In `/etc/gitlab/gitlab.rb`:
+
+```ruby
+roles ['redis_sentinel_role', 'redis_replica_role']
+redis['bind'] = '10.0.0.2'
+redis['port'] = 6379
+redis['password'] = 'redis-password-goes-here'
+redis['master_password'] = 'redis-password-goes-here'
+redis['master_ip'] = '10.0.0.1' # IP of primary Redis server
+#redis['master_port'] = 6379 # Port of primary Redis server, uncomment to change to non default
+redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
+sentinel['bind'] = '10.0.0.2'
+# sentinel['port'] = 26379 # uncomment to change default port
+sentinel['quorum'] = 2
+# sentinel['down_after_milliseconds'] = 10000
+# sentinel['failover_timeout'] = 60000
+```
+
+[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+### Example configuration for Redis replica 2 and Sentinel 3
+
+In `/etc/gitlab/gitlab.rb`:
+
+```ruby
+roles ['redis_sentinel_role', 'redis_replica_role']
+redis['bind'] = '10.0.0.3'
+redis['port'] = 6379
+redis['password'] = 'redis-password-goes-here'
+redis['master_password'] = 'redis-password-goes-here'
+redis['master_ip'] = '10.0.0.1' # IP of primary Redis server
+#redis['master_port'] = 6379 # Port of primary Redis server, uncomment to change to non default
+redis['master_name'] = 'gitlab-redis' # must be the same in every sentinel node
+sentinel['bind'] = '10.0.0.3'
+# sentinel['port'] = 26379 # uncomment to change default port
+sentinel['quorum'] = 2
+# sentinel['down_after_milliseconds'] = 10000
+# sentinel['failover_timeout'] = 60000
+```
+
+[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+### Example configuration for the GitLab application
+
+In `/etc/gitlab/gitlab.rb`:
+
+```ruby
+redis['master_name'] = 'gitlab-redis'
+redis['master_password'] = 'redis-password-goes-here'
+gitlab_rails['redis_sentinels'] = [
+ {'host' => '10.0.0.1', 'port' => 26379},
+ {'host' => '10.0.0.2', 'port' => 26379},
+ {'host' => '10.0.0.3', 'port' => 26379}
+]
+```
+
+[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+
+## Advanced configuration
+
+Omnibus GitLab configures some things behind the curtains to make the sysadmins'
+lives easier. If you want to know what happens underneath keep reading.
+
+### Running multiple Redis clusters
+
+GitLab supports running [separate Redis clusters for different persistent
+classes](https://docs.gitlab.com/omnibus/settings/redis.html#running-with-multiple-redis-instances):
+cache, queues, and shared_state. To make this work with Sentinel:
+
+1. Set the appropriate variable in `/etc/gitlab/gitlab.rb` for each instance you are using:
+
+ ```ruby
+ gitlab_rails['redis_cache_instance'] = REDIS_CACHE_URL
+ gitlab_rails['redis_queues_instance'] = REDIS_QUEUES_URL
+ gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL
+ ```
+
+ **Note**: Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_PRIMARY_NAME`
+
+ 1. PASSWORD is the plaintext password for the Redis instance
+ 1. SENTINEL_PRIMARY_NAME is the Sentinel primary name (e.g. `gitlab-redis-cache`)
+
+1. Include an array of hashes with host/port combinations, such as the following:
+
+ ```ruby
+ gitlab_rails['redis_cache_sentinels'] = [
+ { host: REDIS_CACHE_SENTINEL_HOST, port: PORT1 },
+ { host: REDIS_CACHE_SENTINEL_HOST2, port: PORT2 }
+ ]
+ gitlab_rails['redis_queues_sentinels'] = [
+ { host: REDIS_QUEUES_SENTINEL_HOST, port: PORT1 },
+ { host: REDIS_QUEUES_SENTINEL_HOST2, port: PORT2 }
+ ]
+ gitlab_rails['redis_shared_state_sentinels'] = [
+ { host: SHARED_STATE_SENTINEL_HOST, port: PORT1 },
+ { host: SHARED_STATE_SENTINEL_HOST2, port: PORT2 }
+ ]
+ ```
+
+1. Note that for each persistence class, GitLab will default to using the
+ configuration specified in `gitlab_rails['redis_sentinels']` unless
+ overridden by the settings above.
+1. Be sure to include BOTH configuration options for each persistent classes. For example,
+ if you choose to configure a cache instance, you must specify both `gitlab_rails['redis_cache_instance']`
+ and `gitlab_rails['redis_cache_sentinels']` for GitLab to generate the proper configuration files.
+1. Run `gitlab-ctl reconfigure`
+
+### Control running services
+
+In the previous example, we've used `redis_sentinel_role` and
+`redis_master_role` which simplifies the amount of configuration changes.
+
+If you want more control, here is what each one sets for you automatically
+when enabled:
+
+```ruby
+## Redis Sentinel Role
+redis_sentinel_role['enable'] = true
+
+# When Sentinel Role is enabled, the following services are also enabled
+sentinel['enable'] = true
+
+# The following services are disabled
+redis['enable'] = false
+bootstrap['enable'] = false
+nginx['enable'] = false
+postgresql['enable'] = false
+gitlab_rails['enable'] = false
+mailroom['enable'] = false
+
+-------
+
+## Redis primary/replica Role
+redis_master_role['enable'] = true # enable only one of them
+redis_replica_role['enable'] = true # enable only one of them
+
+# When Redis primary or Replica role are enabled, the following services are
+# enabled/disabled. Note that if Redis and Sentinel roles are combined, both
+# services will be enabled.
+
+# The following services are disabled
+sentinel['enable'] = false
+bootstrap['enable'] = false
+nginx['enable'] = false
+postgresql['enable'] = false
+gitlab_rails['enable'] = false
+mailroom['enable'] = false
+
+# For Redis Replica role, also change this setting from default 'true' to 'false':
+redis['master'] = false
+```
+
+You can find the relevant attributes defined in [`gitlab_rails.rb`](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb).
+
+## Troubleshooting
+
+See the [Redis troubleshooting guide](troubleshooting.md).
+
+## Further reading
+
+Read more:
+
+1. [Reference architectures](../reference_architectures/index.md)
+1. [Configure the database](../postgresql/replication_and_failover.md)
+1. [Configure NFS](../high_availability/nfs.md)
+1. [Configure the GitLab application servers](../high_availability/gitlab.md)
+1. [Configure the load balancers](../high_availability/load_balancer.md)
diff --git a/doc/administration/redis/replication_and_failover_external.md b/doc/administration/redis/replication_and_failover_external.md
new file mode 100644
index 00000000000..244b44dd76a
--- /dev/null
+++ b/doc/administration/redis/replication_and_failover_external.md
@@ -0,0 +1,376 @@
+---
+type: howto
+stage: Enablement
+group: Distribution
+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/#designated-technical-writers
+---
+
+# Redis replication and failover providing your own instance **(CORE ONLY)**
+
+If you’re hosting GitLab on a cloud provider, you can optionally use a managed
+service for Redis. For example, AWS offers ElastiCache that runs Redis.
+
+Alternatively, you may opt to manage your own Redis instance separate from the
+Omnibus GitLab package.
+
+## Requirements
+
+The following are the requirements for providing your own Redis instance:
+
+- Redis version 5.0 or higher is recommended, as this is what ships with
+ Omnibus GitLab packages starting with GitLab 12.7.
+- Support for Redis 3.2 is deprecated with GitLab 12.10 and will be completely
+ removed in GitLab 13.0.
+- GitLab 12.0 and later requires Redis version 3.2 or higher. Older Redis
+ versions do not support an optional count argument to SPOP which is now
+ required for [Merge Trains](../../ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md).
+- In addition, if Redis 4 or later is available, GitLab makes use of certain
+ commands like `UNLINK` and `USAGE` which were introduced only in Redis 4.
+- Standalone Redis or Redis high availability with Sentinel are supported. Redis
+ Cluster is not supported.
+- Managed Redis from cloud providers such as AWS ElastiCache will work. If these
+ services support high availability, be sure it is **not** the Redis Cluster type.
+
+Note the Redis node's IP address or hostname, port, and password (if required).
+
+## Redis as a managed service in a cloud provider
+
+1. Set up Redis according to the [requirements](#requirements).
+1. Configure the GitLab application servers with the appropriate connection details
+ for your external Redis service in your `/etc/gitlab/gitlab.rb` file:
+
+ ```ruby
+ redis['enable'] = false
+
+ gitlab_rails['redis_host'] = 'redis.example.com'
+ gitlab_rails['redis_port'] = 6379
+
+ # Required if Redis authentication is configured on the Redis node
+ gitlab_rails['redis_password'] = 'Redis Password'
+ ```
+
+1. Reconfigure for the changes to take effect:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+## Redis replication and failover with your own Redis servers
+
+This is the documentation for configuring a scalable Redis setup when
+you have installed Redis all by yourself and not using the bundled one that
+comes with the Omnibus packages, although using the Omnibus GitLab packages is
+highly recommend as we optimize them specifically for GitLab, and we will take
+care of upgrading Redis to the latest supported version.
+
+Note also that you may elect to override all references to
+`/home/git/gitlab/config/resque.yml` in accordance with the advanced Redis
+settings outlined in
+[Configuration Files Documentation](https://gitlab.com/gitlab-org/gitlab/blob/master/config/README.md).
+
+We cannot stress enough the importance of reading the
+[replication and failover](replication_and_failover.md) documentation of the
+Omnibus Redis HA as it provides some invaluable information to the configuration
+of Redis. Please proceed to read it before going forward with this guide.
+
+Before proceeding on setting up the new Redis instances, here are some
+requirements:
+
+- All Redis servers in this guide must be configured to use a TCP connection
+ instead of a socket. To configure Redis to use TCP connections you need to
+ define both `bind` and `port` in the Redis config file. You can bind to all
+ interfaces (`0.0.0.0`) or specify the IP of the desired interface
+ (e.g., one from an internal network).
+- Since Redis 3.2, you must define a password to receive external connections
+ (`requirepass`).
+- If you are using Redis with Sentinel, you will also need to define the same
+ password for the replica password definition (`masterauth`) in the same instance.
+
+In addition, read the prerequisites as described in the
+[Omnibus Redis document](replication_and_failover.md#requirements) since they provide some
+valuable information for the general setup.
+
+### Step 1. Configuring the primary Redis instance
+
+Assuming that the Redis primary instance IP is `10.0.0.1`:
+
+1. [Install Redis](../../install/installation.md#7-redis).
+1. Edit `/etc/redis/redis.conf`:
+
+ ```conf
+ ## Define a `bind` address pointing to a local IP that your other machines
+ ## can reach you. If you really need to bind to an external accessible IP, make
+ ## sure you add extra firewall rules to prevent unauthorized access:
+ bind 10.0.0.1
+
+ ## Define a `port` to force redis to listen on TCP so other machines can
+ ## connect to it (default port is `6379`).
+ port 6379
+
+ ## Set up password authentication (use the same password in all nodes).
+ ## The password should be defined equal for both `requirepass` and `masterauth`
+ ## when setting up Redis to use with Sentinel.
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+
+### Step 2. Configuring the replica Redis instances
+
+Assuming that the Redis replica instance IP is `10.0.0.2`:
+
+1. [Install Redis](../../install/installation.md#7-redis).
+1. Edit `/etc/redis/redis.conf`:
+
+ ```conf
+ ## Define a `bind` address pointing to a local IP that your other machines
+ ## can reach you. If you really need to bind to an external accessible IP, make
+ ## sure you add extra firewall rules to prevent unauthorized access:
+ bind 10.0.0.2
+
+ ## Define a `port` to force redis to listen on TCP so other machines can
+ ## connect to it (default port is `6379`).
+ port 6379
+
+ ## Set up password authentication (use the same password in all nodes).
+ ## The password should be defined equal for both `requirepass` and `masterauth`
+ ## when setting up Redis to use with Sentinel.
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+
+ ## Define `replicaof` pointing to the Redis primary instance with IP and port.
+ replicaof 10.0.0.1 6379
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+1. Go through the steps again for all the other replica nodes.
+
+### Step 3. Configuring the Redis Sentinel instances
+
+Sentinel is a special type of Redis server. It inherits most of the basic
+configuration options you can define in `redis.conf`, with specific ones
+starting with `sentinel` prefix.
+
+Assuming that the Redis Sentinel is installed on the same instance as Redis
+primary with IP `10.0.0.1` (some settings might overlap with the primary):
+
+1. [Install Redis Sentinel](https://redis.io/topics/sentinel).
+1. Edit `/etc/redis/sentinel.conf`:
+
+ ```conf
+ ## Define a `bind` address pointing to a local IP that your other machines
+ ## can reach you. If you really need to bind to an external accessible IP, make
+ ## sure you add extra firewall rules to prevent unauthorized access:
+ bind 10.0.0.1
+
+ ## Define a `port` to force Sentinel to listen on TCP so other machines can
+ ## connect to it (default port is `6379`).
+ port 26379
+
+ ## Set up password authentication (use the same password in all nodes).
+ ## The password should be defined equal for both `requirepass` and `masterauth`
+ ## when setting up Redis to use with Sentinel.
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+
+ ## Define with `sentinel auth-pass` the same shared password you have
+ ## defined for both Redis primary and replicas instances.
+ sentinel auth-pass gitlab-redis redis-password-goes-here
+
+ ## Define with `sentinel monitor` the IP and port of the Redis
+ ## primary node, and the quorum required to start a failover.
+ sentinel monitor gitlab-redis 10.0.0.1 6379 2
+
+ ## Define with `sentinel down-after-milliseconds` the time in `ms`
+ ## that an unresponsive server will be considered down.
+ sentinel down-after-milliseconds gitlab-redis 10000
+
+ ## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
+ ## meanings:
+ ##
+ ## * The time needed to re-start a failover after a previous failover was
+ ## already tried against the same primary by a given Sentinel, is two
+ ## times the failover timeout.
+ ##
+ ## * The time needed for a replica replicating to a wrong primary according
+ ## to a Sentinel current configuration, to be forced to replicate
+ ## with the right primary, is exactly the failover timeout (counting since
+ ## the moment a Sentinel detected the misconfiguration).
+ ##
+ ## * The time needed to cancel a failover that is already in progress but
+ ## did not produced any configuration change (REPLICAOF NO ONE yet not
+ ## acknowledged by the promoted replica).
+ ##
+ ## * The maximum time a failover in progress waits for all the replicas to be
+ ## reconfigured as replicas of the new primary. However even after this time
+ ## the replicas will be reconfigured by the Sentinels anyway, but not with
+ ## the exact parallel-syncs progression as specified.
+ sentinel failover_timeout 30000
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+1. Go through the steps again for all the other Sentinel nodes.
+
+### Step 4. Configuring the GitLab application
+
+You can enable or disable Sentinel support at any time in new or existing
+installations. From the GitLab application perspective, all it requires is
+the correct credentials for the Sentinel nodes.
+
+While it doesn't require a list of all Sentinel nodes, in case of a failure,
+it needs to access at least one of listed ones.
+
+The following steps should be performed in the [GitLab application server](../high_availability/gitlab.md)
+which ideally should not have Redis or Sentinels in the same machine:
+
+1. Edit `/home/git/gitlab/config/resque.yml` following the example in
+ [resque.yml.example](https://gitlab.com/gitlab-org/gitlab/blob/master/config/resque.yml.example), and uncomment the Sentinel lines, pointing to
+ the correct server credentials:
+
+ ```yaml
+ # resque.yaml
+ production:
+ url: redis://:redi-password-goes-here@gitlab-redis/
+ sentinels:
+ -
+ host: 10.0.0.1
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.2
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.3
+ port: 26379 # point to sentinel, not to redis port
+ ```
+
+1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
+
+## Example of minimal configuration with 1 primary, 2 replicas and 3 sentinels
+
+In this example we consider that all servers have an internal network
+interface with IPs in the `10.0.0.x` range, and that they can connect
+to each other using these IPs.
+
+In a real world usage, you would also set up firewall rules to prevent
+unauthorized access from other machines, and block traffic from the
+outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
+
+For this example, **Sentinel 1** will be configured in the same machine as the
+**Redis Primary**, **Sentinel 2** and **Sentinel 3** in the same machines as the
+**Replica 1** and **Replica 2** respectively.
+
+Here is a list and description of each **machine** and the assigned **IP**:
+
+- `10.0.0.1`: Redis Primary + Sentinel 1
+- `10.0.0.2`: Redis Replica 1 + Sentinel 2
+- `10.0.0.3`: Redis Replica 2 + Sentinel 3
+- `10.0.0.4`: GitLab application
+
+Please note that after the initial configuration, if a failover is initiated
+by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
+will change permanently (including in `redis.conf`) from one node to the other,
+until a new failover is initiated again.
+
+The same thing will happen with `sentinel.conf` that will be overridden after the
+initial execution, after any new sentinel node starts watching the **Primary**,
+or a failover promotes a different **Primary** node.
+
+### Example configuration for Redis primary and Sentinel 1
+
+1. In `/etc/redis/redis.conf`:
+
+ ```conf
+ bind 10.0.0.1
+ port 6379
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+ ```
+
+1. In `/etc/redis/sentinel.conf`:
+
+ ```conf
+ bind 10.0.0.1
+ port 26379
+ sentinel auth-pass gitlab-redis redis-password-goes-here
+ sentinel monitor gitlab-redis 10.0.0.1 6379 2
+ sentinel down-after-milliseconds gitlab-redis 10000
+ sentinel failover_timeout 30000
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+
+### Example configuration for Redis replica 1 and Sentinel 2
+
+1. In `/etc/redis/redis.conf`:
+
+ ```conf
+ bind 10.0.0.2
+ port 6379
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+ replicaof 10.0.0.1 6379
+ ```
+
+1. In `/etc/redis/sentinel.conf`:
+
+ ```conf
+ bind 10.0.0.2
+ port 26379
+ sentinel auth-pass gitlab-redis redis-password-goes-here
+ sentinel monitor gitlab-redis 10.0.0.1 6379 2
+ sentinel down-after-milliseconds gitlab-redis 10000
+ sentinel failover_timeout 30000
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+
+### Example configuration for Redis replica 2 and Sentinel 3
+
+1. In `/etc/redis/redis.conf`:
+
+ ```conf
+ bind 10.0.0.3
+ port 6379
+ requirepass redis-password-goes-here
+ masterauth redis-password-goes-here
+ replicaof 10.0.0.1 6379
+ ```
+
+1. In `/etc/redis/sentinel.conf`:
+
+ ```conf
+ bind 10.0.0.3
+ port 26379
+ sentinel auth-pass gitlab-redis redis-password-goes-here
+ sentinel monitor gitlab-redis 10.0.0.1 6379 2
+ sentinel down-after-milliseconds gitlab-redis 10000
+ sentinel failover_timeout 30000
+ ```
+
+1. Restart the Redis service for the changes to take effect.
+
+### Example configuration of the GitLab application
+
+1. Edit `/home/git/gitlab/config/resque.yml`:
+
+ ```yaml
+ production:
+ url: redis://:redi-password-goes-here@gitlab-redis/
+ sentinels:
+ -
+ host: 10.0.0.1
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.2
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.3
+ port: 26379 # point to sentinel, not to redis port
+ ```
+
+1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
+
+## Troubleshooting
+
+See the [Redis troubleshooting guide](troubleshooting.md).
diff --git a/doc/administration/redis/standalone.md b/doc/administration/redis/standalone.md
new file mode 100644
index 00000000000..12e932dbc5e
--- /dev/null
+++ b/doc/administration/redis/standalone.md
@@ -0,0 +1,63 @@
+---
+type: howto
+stage: Enablement
+group: Distribution
+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/#designated-technical-writers
+---
+
+# Standalone Redis using Omnibus GitLab **(CORE ONLY)**
+
+The Omnibus GitLab package can be used to configure a standalone Redis server.
+In this configuration, Redis is not scaled, and represents a single
+point of failure. However, in a scaled environment the objective is to allow
+the environment to handle more users or to increase throughput. Redis itself
+is generally stable and can handle many requests, so it is an acceptable
+trade off to have only a single instance. See the [reference architectures](../reference_architectures/index.md)
+page for an overview of GitLab scaling options.
+
+## Set up a standalone Redis instance
+
+The steps below are the minimum necessary to configure a Redis server with
+Omnibus GitLab:
+
+1. SSH into the Redis server.
+1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
+ package you want by using **steps 1 and 2** from the GitLab downloads page.
+ Do not complete any other steps on the download page.
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+
+ ```ruby
+ ## Enable Redis
+ redis['enable'] = true
+
+ ## Disable all other services
+ sidekiq['enable'] = false
+ gitlab_workhorse['enable'] = false
+ puma['enable'] = false
+ postgresql['enable'] = false
+ nginx['enable'] = false
+ prometheus['enable'] = false
+ alertmanager['enable'] = false
+ pgbouncer_exporter['enable'] = false
+ gitlab_exporter['enable'] = false
+ gitaly['enable'] = false
+
+ redis['bind'] = '0.0.0.0'
+ redis['port'] = 6379
+ redis['password'] = 'SECRET_PASSWORD_HERE'
+
+ gitlab_rails['enable'] = false
+ ```
+
+1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Note the Redis node's IP address or hostname, port, and
+ Redis password. These will be necessary when configuring the GitLab
+ application servers later.
+
+[Advanced configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
+are supported and can be added if needed.
+
+## Troubleshooting
+
+See the [Redis troubleshooting guide](troubleshooting.md).
diff --git a/doc/administration/redis/troubleshooting.md b/doc/administration/redis/troubleshooting.md
new file mode 100644
index 00000000000..402b60e5b7b
--- /dev/null
+++ b/doc/administration/redis/troubleshooting.md
@@ -0,0 +1,158 @@
+---
+type: reference
+stage: Enablement
+group: Distribution
+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/#designated-technical-writers
+---
+
+# Troubleshooting Redis
+
+There are a lot of moving parts that needs to be taken care carefully
+in order for the HA setup to work as expected.
+
+Before proceeding with the troubleshooting below, check your firewall rules:
+
+- Redis machines
+ - Accept TCP connection in `6379`
+ - Connect to the other Redis machines via TCP in `6379`
+- Sentinel machines
+ - Accept TCP connection in `26379`
+ - Connect to other Sentinel machines via TCP in `26379`
+ - Connect to the Redis machines via TCP in `6379`
+
+## Troubleshooting Redis replication
+
+You can check if everything is correct by connecting to each server using
+`redis-cli` application, and sending the `info replication` command as below.
+
+```shell
+/opt/gitlab/embedded/bin/redis-cli -h <redis-host-or-ip> -a '<redis-password>' info replication
+```
+
+When connected to a `Primary` Redis, you will see the number of connected
+`replicas`, and a list of each with connection details:
+
+```plaintext
+# Replication
+role:master
+connected_replicas:1
+replica0:ip=10.133.5.21,port=6379,state=online,offset=208037514,lag=1
+master_repl_offset:208037658
+repl_backlog_active:1
+repl_backlog_size:1048576
+repl_backlog_first_byte_offset:206989083
+repl_backlog_histlen:1048576
+```
+
+When it's a `replica`, you will see details of the primary connection and if
+its `up` or `down`:
+
+```plaintext
+# Replication
+role:replica
+master_host:10.133.1.58
+master_port:6379
+master_link_status:up
+master_last_io_seconds_ago:1
+master_sync_in_progress:0
+replica_repl_offset:208096498
+replica_priority:100
+replica_read_only:1
+connected_replicas:0
+master_repl_offset:0
+repl_backlog_active:0
+repl_backlog_size:1048576
+repl_backlog_first_byte_offset:0
+repl_backlog_histlen:0
+```
+
+## Troubleshooting Sentinel
+
+If you get an error like: `Redis::CannotConnectError: No sentinels available.`,
+there may be something wrong with your configuration files or it can be related
+to [this issue](https://github.com/redis/redis-rb/issues/531).
+
+You must make sure you are defining the same value in `redis['master_name']`
+and `redis['master_pasword']` as you defined for your sentinel node.
+
+The way the Redis connector `redis-rb` works with sentinel is a bit
+non-intuitive. We try to hide the complexity in omnibus, but it still requires
+a few extra configurations.
+
+---
+
+To make sure your configuration is correct:
+
+1. SSH into your GitLab application server
+1. Enter the Rails console:
+
+ ```shell
+ # For Omnibus installations
+ sudo gitlab-rails console
+
+ # For source installations
+ sudo -u git rails console -e production
+ ```
+
+1. Run in the console:
+
+ ```ruby
+ redis = Redis.new(Gitlab::Redis::SharedState.params)
+ redis.info
+ ```
+
+ Keep this screen open and try to simulate a failover below.
+
+1. To simulate a failover on primary Redis, SSH into the Redis server and run:
+
+ ```shell
+ # port must match your primary redis port, and the sleep time must be a few seconds bigger than defined one
+ redis-cli -h localhost -p 6379 DEBUG sleep 20
+ ```
+
+1. Then back in the Rails console from the first step, run:
+
+ ```ruby
+ redis.info
+ ```
+
+ You should see a different port after a few seconds delay
+ (the failover/reconnect time).
+
+## Troubleshooting a non-bundled Redis with an installation from source
+
+If you get an error in GitLab like `Redis::CannotConnectError: No sentinels available.`,
+there may be something wrong with your configuration files or it can be related
+to [this upstream issue](https://github.com/redis/redis-rb/issues/531).
+
+You must make sure that `resque.yml` and `sentinel.conf` are configured correctly,
+otherwise `redis-rb` will not work properly.
+
+The `master-group-name` (`gitlab-redis`) defined in (`sentinel.conf`)
+**must** be used as the hostname in GitLab (`resque.yml`):
+
+```conf
+# sentinel.conf:
+sentinel monitor gitlab-redis 10.0.0.1 6379 2
+sentinel down-after-milliseconds gitlab-redis 10000
+sentinel config-epoch gitlab-redis 0
+sentinel leader-epoch gitlab-redis 0
+```
+
+```yaml
+# resque.yaml
+production:
+ url: redis://:myredispassword@gitlab-redis/
+ sentinels:
+ -
+ host: 10.0.0.1
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.2
+ port: 26379 # point to sentinel, not to redis port
+ -
+ host: 10.0.0.3
+ port: 26379 # point to sentinel, not to redis port
+```
+
+When in doubt, read the [Redis Sentinel documentation](https://redis.io/topics/sentinel).
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index a15fcf722a5..5367021af4e 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -47,7 +47,7 @@ For a full list of reference architectures, see
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 34805a8ac68..def23619a5c 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -57,7 +57,7 @@ added performance and reliability at a reduced complexity cost.
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index d851fa124c6..17f4300eb03 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -47,7 +47,7 @@ For a full list of reference architectures, see
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index efeed3e9ffd..b81855b9451 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -50,7 +50,7 @@ following the [2,000-user reference architecture](2k_users.md).
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index dd94f5470b4..2540fe68dac 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -47,7 +47,7 @@ For a full list of reference architectures, see
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 604572b083e..fbb9c32442a 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -44,7 +44,7 @@ For a full list of reference architectures, see
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index 623d7f3f776..d42263c6ce1 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -209,7 +209,7 @@ cluster with the Rails nodes broken down into a number of smaller Pods across th
For medium sized installs (3,000 - 5,000) we suggest one Redis cluster for all
classes and that Redis Sentinel is hosted alongside Consul.
For larger architectures (10,000 users or more) we suggest running a separate
- [Redis Cluster](../high_availability/redis.md#running-multiple-redis-clusters) for the Cache class
+ [Redis Cluster](../redis/replication_and_failover.md#running-multiple-redis-clusters) for the Cache class
and another for the Queues and Shared State classes respectively. We also recommend
that you run the Redis Sentinel clusters separately for each Redis Cluster.
diff --git a/doc/api/users.md b/doc/api/users.md
index 6ac1cd089e7..f32aef89066 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -799,6 +799,9 @@ Parameters:
- `key` (required) - new SSH key
- `expires_at` (optional) - The expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)
+NOTE: **Note:**
+This also adds an audit event, as described in [audit instance events](../administration/audit_events.md#instance-events-premium-only). **(PREMIUM)**
+
## Delete SSH key for current user
Deletes key owned by currently authenticated user.
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index 813e343f2cc..83030b6d01d 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -360,7 +360,7 @@ persistence and is used to store session data, temporary cache information, and
1. Navigate back to the ElastiCache dashboard.
1. Select **Redis** on the left menu and click **Create** to create a new
- Redis cluster. Do not enable **Cluster Mode** as it is [not supported](../../administration/high_availability/redis.md#provide-your-own-redis-instance-core-only). Even without cluster mode on, you still get the
+ Redis cluster. Do not enable **Cluster Mode** as it is [not supported](../../administration/redis/replication_and_failover_external.md#requirements). Even without cluster mode on, you still get the
chance to deploy Redis in multiple availability zones.
1. In the settings section:
1. Give the cluster a name (`gitlab-redis`) and a description.
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 5a9fad3be56..e59494c9d9c 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -3,8 +3,6 @@
module Gitlab
module BitbucketImport
class Importer
- include Gitlab::BitbucketImport::Metrics
-
LABELS = [{ title: 'bug', color: '#FF0000' },
{ title: 'enhancement', color: '#428BCA' },
{ title: 'proposal', color: '#69D100' },
@@ -26,6 +24,7 @@ module Gitlab
import_issues
import_pull_requests
handle_errors
+ metrics.track_finished_import
true
end
@@ -115,6 +114,8 @@ module Gitlab
updated_at: issue.updated_at
)
+ metrics.issues_counter.increment
+
gitlab_issue.labels << @labels[label_name]
import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted?
@@ -195,6 +196,8 @@ module Gitlab
updated_at: pull_request.updated_at
)
+ metrics.merge_requests_counter.increment
+
import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
rescue StandardError => e
store_pull_request_error(pull_request, e)
@@ -288,6 +291,10 @@ module Gitlab
project_path: project.full_path
}
end
+
+ def metrics
+ @metrics ||= Gitlab::Import::Metrics.new(:bitbucket_importer, @project)
+ end
end
end
end
diff --git a/lib/gitlab/bitbucket_import/metrics.rb b/lib/gitlab/bitbucket_import/metrics.rb
deleted file mode 100644
index 25e2d9b211e..00000000000
--- a/lib/gitlab/bitbucket_import/metrics.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module BitbucketImport
- module Metrics
- extend ActiveSupport::Concern
-
- IMPORTER = :bitbucket_importer
-
- included do
- prepend Gitlab::Import::Metrics
-
- Gitlab::Import::Metrics.measure(:execute, metrics: {
- "#{IMPORTER}_imported_projects": {
- type: :counter,
- description: 'The number of imported Bitbucket projects'
- },
- "#{IMPORTER}_total_duration_seconds": {
- type: :histogram,
- labels: { importer: IMPORTER },
- description: 'Total time spent importing Bitbucket projects, in seconds'
- }
- })
-
- Gitlab::Import::Metrics.measure(:import_issue, metrics: {
- "#{IMPORTER}_imported_issues": {
- type: :counter,
- description: 'The number of imported Bitbucket issues'
- }
- })
-
- Gitlab::Import::Metrics.measure(:import_pull_request, metrics: {
- "#{IMPORTER}_imported_pull_requests": {
- type: :counter,
- description: 'The number of imported Bitbucket pull requests'
- }
- })
- end
- end
- end
-end
diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb
index 16fe5b46b1f..18a1b64729e 100644
--- a/lib/gitlab/bitbucket_server_import/importer.rb
+++ b/lib/gitlab/bitbucket_server_import/importer.rb
@@ -43,6 +43,7 @@ module Gitlab
import_pull_requests
delete_temp_branches
handle_errors
+ metrics.track_finished_import
log_info(stage: "complete")
@@ -219,7 +220,11 @@ module Gitlab
creator = Gitlab::Import::MergeRequestCreator.new(project)
merge_request = creator.execute(attributes)
- import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
+ if merge_request.persisted?
+ import_pull_request_comments(pull_request, merge_request)
+
+ metrics.merge_requests_counter.increment
+ end
log_info(stage: 'import_bitbucket_pull_requests', message: 'finished', iid: pull_request.iid)
end
@@ -388,6 +393,10 @@ module Gitlab
project_path: project.full_path
}
end
+
+ def metrics
+ @metrics ||= Gitlab::Import::Metrics.new(:bitbucket_server_importer, @project)
+ end
end
end
end
diff --git a/lib/gitlab/import/metrics.rb b/lib/gitlab/import/metrics.rb
index 76638a8cf86..2692ab2fa12 100644
--- a/lib/gitlab/import/metrics.rb
+++ b/lib/gitlab/import/metrics.rb
@@ -1,59 +1,54 @@
# frozen_string_literal: true
-# Prepend `Gitlab::Import::Metrics` to a class in order
-# to measure and emit `Gitlab::Metrics` metrics of specified methods.
-#
-# @example
-# class Importer
-# prepend Gitlab::Import::Metrics
-#
-# Gitlab::ImportExport::Metrics.measure :execute, metrics: {
-# importer_counter: {
-# type: :counter,
-# description: 'counter'
-# },
-# importer_histogram: {
-# type: :histogram,
-# labels: { importer: 'importer' },
-# description: 'histogram'
-# }
-# }
-#
-# def execute
-# ...
-# end
-# end
-#
-# Each call to `#execute` increments `importer_counter` as well as
-# measures `#execute` duration and reports histogram `importer_histogram`
module Gitlab
module Import
- module Metrics
- def self.measure(method_name, metrics:)
- define_method "#{method_name}" do |*args|
- start_time = Time.zone.now
+ class Metrics
+ IMPORT_DURATION_BUCKETS = [0.5, 1, 3, 5, 10, 60, 120, 240, 360, 720, 1440].freeze
- result = super(*args)
+ attr_reader :importer
- end_time = Time.zone.now
+ def initialize(importer, project)
+ @importer = importer
+ @project = project
+ end
+
+ def track_finished_import
+ duration = Time.zone.now - @project.created_at
+
+ duration_histogram.observe({ importer: importer }, duration)
+ projects_counter.increment
+ end
- report_measurement_metrics(metrics, end_time - start_time)
+ def projects_counter
+ @projects_counter ||= Gitlab::Metrics.counter(
+ :"#{importer}_imported_projects_total",
+ 'The number of imported projects'
+ )
+ end
+
+ def issues_counter
+ @issues_counter ||= Gitlab::Metrics.counter(
+ :"#{importer}_imported_issues_total",
+ 'The number of imported issues'
+ )
+ end
- result
- end
+ def merge_requests_counter
+ @merge_requests_counter ||= Gitlab::Metrics.counter(
+ :"#{importer}_imported_merge_requests_total",
+ 'The number of imported merge (pull) requests'
+ )
end
- def report_measurement_metrics(metrics, duration)
- metrics.each do |metric_name, metric_value|
- case metric_value[:type]
- when :counter
- Gitlab::Metrics.counter(metric_name, metric_value[:description]).increment
- when :histogram
- Gitlab::Metrics.histogram(metric_name, metric_value[:description]).observe(metric_value[:labels], duration)
- else
- nil
- end
- end
+ private
+
+ def duration_histogram
+ @duration_histogram ||= Gitlab::Metrics.histogram(
+ :"#{importer}_total_duration_seconds",
+ 'Total time spent importing projects, in seconds',
+ {},
+ IMPORT_DURATION_BUCKETS
+ )
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bacfd5f9e37..11747bbeae9 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -15113,7 +15113,7 @@ msgstr ""
msgid "No test coverage"
msgstr ""
-msgid "No thanks, don't show this again"
+msgid "No thanks"
msgstr ""
msgid "No vulnerabilities present"
@@ -20598,7 +20598,7 @@ msgstr ""
msgid "Show me everything"
msgstr ""
-msgid "Show me how"
+msgid "Show me how to add a pipeline"
msgstr ""
msgid "Show me more advanced stuff"
@@ -27218,9 +27218,6 @@ msgstr ""
msgid "mrWidget|Deployment statistics are not available currently"
msgstr ""
-msgid "mrWidget|Detect issues before deployment with a CI pipeline that continuously tests your code. We created a quick guide that will show you how to create one. Make your code more secure and more robust in just a minute."
-msgstr ""
-
msgid "mrWidget|Did not close"
msgstr ""
@@ -27383,6 +27380,9 @@ msgstr ""
msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
msgstr ""
+msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd}, simply add a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust."
+msgstr ""
+
msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
msgstr ""
diff --git a/package.json b/package.json
index f6247c79f78..87252948738 100644
--- a/package.json
+++ b/package.json
@@ -46,8 +46,8 @@
"@rails/actioncable": "^6.0.3-1",
"@sentry/browser": "^5.10.2",
"@sourcegraph/code-host-integration": "0.0.48",
- "@toast-ui/editor": "2.1.2",
- "@toast-ui/vue-editor": "2.1.2",
+ "@toast-ui/editor": "^2.2.0",
+ "@toast-ui/vue-editor": "^2.2.0",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
"apollo-link": "^1.2.11",
@@ -155,10 +155,10 @@
"xterm": "^3.5.0"
},
"devDependencies": {
- "acorn": "^6.3.0",
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
"@gitlab/eslint-plugin": "3.1.0",
"@vue/test-utils": "^1.0.0-beta.30",
+ "acorn": "^6.3.0",
"axios-mock-adapter": "^1.15.0",
"babel-jest": "^24.1.0",
"babel-plugin-dynamic-import-node": "^2.2.0",
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 2322babcd53..b78fc63b7dd 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -174,7 +174,7 @@ RSpec.describe 'Database schema' do
IGNORED_JSONB_COLUMNS = {
"ApplicationSetting" => %w[repository_storages_weighted],
"AlertManagement::Alert" => %w[payload],
- "Ci::BuildMetadata" => %w[config_options config_variables],
+ "Ci::BuildMetadata" => %w[config_options config_variables secrets], # secrets has an EE-only validator
"Geo::Event" => %w[payload],
"GeoNodeStatus" => %w[status],
"Operations::FeatureFlagScope" => %w[strategies],
diff --git a/spec/frontend/boards/board_card_spec.js b/spec/frontend/boards/board_card_spec.js
index 959c71d05ca..6bfbc7cb12f 100644
--- a/spec/frontend/boards/board_card_spec.js
+++ b/spec/frontend/boards/board_card_spec.js
@@ -196,7 +196,7 @@ describe('Board card', () => {
wrapper.trigger('mousedown');
wrapper.trigger('mouseup');
- expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', wrapper.vm.issue, undefined);
+ expect(eventHub.$emit).toHaveBeenCalledWith('newDetailIssue', [wrapper.vm.issue, undefined]);
expect(boardsStore.detail.list).toEqual(wrapper.vm.list);
});
diff --git a/spec/frontend/cycle_analytics/stage_nav_item_spec.js b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
index 480bb756731..1fe80d3b1ce 100644
--- a/spec/frontend/cycle_analytics/stage_nav_item_spec.js
+++ b/spec/frontend/cycle_analytics/stage_nav_item_spec.js
@@ -10,7 +10,6 @@ describe('StageNavItem', () => {
const func = shallow ? shallowMount : mount;
return func(StageNavItem, {
propsData: {
- canEdit: false,
isActive: false,
isUserAllowed: false,
isDefaultStage: true,
@@ -125,7 +124,7 @@ describe('StageNavItem', () => {
describe('User can edit stages', () => {
beforeEach(() => {
- wrapper = createComponent({ canEdit: true, isUserAllowed: true }, false);
+ wrapper = createComponent({ isUserAllowed: true }, false);
});
afterEach(() => {
diff --git a/spec/frontend/deploy_keys/components/action_btn_spec.js b/spec/frontend/deploy_keys/components/action_btn_spec.js
index b8211b02464..78312751429 100644
--- a/spec/frontend/deploy_keys/components/action_btn_spec.js
+++ b/spec/frontend/deploy_keys/components/action_btn_spec.js
@@ -32,7 +32,7 @@ describe('Deploy keys action btn', () => {
wrapper.trigger('click');
return wrapper.vm.$nextTick().then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, expect.anything());
+ expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', [deployKey, expect.any(Function)]);
});
});
diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js
index 291502c9ed7..cf54b7e60aa 100644
--- a/spec/frontend/deploy_keys/components/app_spec.js
+++ b/spec/frontend/deploy_keys/components/app_spec.js
@@ -88,7 +88,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'enableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('enable.key', key);
+ eventHub.$emit('enable.key', [key]);
return wrapper.vm.$nextTick();
})
@@ -106,7 +106,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('disable.key', key);
+ eventHub.$emit('disable.key', [key]);
return wrapper.vm.$nextTick();
})
@@ -124,7 +124,7 @@ describe('Deploy keys app component', () => {
jest.spyOn(wrapper.vm.service, 'getKeys').mockImplementation(() => {});
jest.spyOn(wrapper.vm.service, 'disableKey').mockImplementation(() => Promise.resolve());
- eventHub.$emit('remove.key', key);
+ eventHub.$emit('remove.key', [key]);
return wrapper.vm.$nextTick();
})
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index 35eda21e047..3c04cac7402 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -184,7 +184,7 @@ describe('AppComponent', () => {
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
jest.spyOn($, 'scrollTo').mockImplementation(() => {});
- const fetchPagePromise = vm.fetchPage(2, null, null, true);
+ const fetchPagePromise = vm.fetchPage([2, null, null, true]);
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
@@ -275,7 +275,7 @@ describe('AppComponent', () => {
expect(vm.targetGroup).toBe(null);
expect(vm.targetParentGroup).toBe(null);
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.targetGroup).not.toBe(null);
expect(vm.targetParentGroup).not.toBe(null);
@@ -286,7 +286,7 @@ describe('AppComponent', () => {
expect(vm.showModal).toBe(false);
expect(vm.groupLeaveConfirmationMessage).toBe('');
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.showModal).toBe(true);
expect(vm.groupLeaveConfirmationMessage).toBe(
@@ -298,7 +298,7 @@ describe('AppComponent', () => {
describe('hideLeaveGroupModal', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
const group = { ...mockParentGroupItem };
- vm.showLeaveGroupModal(group, mockParentGroupItem);
+ vm.showLeaveGroupModal([group, mockParentGroupItem]);
expect(vm.showModal).toBe(true);
vm.hideLeaveGroupModal();
diff --git a/spec/frontend/groups/components/groups_spec.js b/spec/frontend/groups/components/groups_spec.js
index 6205400eb03..42c3c813941 100644
--- a/spec/frontend/groups/components/groups_spec.js
+++ b/spec/frontend/groups/components/groups_spec.js
@@ -41,13 +41,12 @@ describe('GroupsComponent', () => {
vm.change(2);
- expect(eventHub.$emit).toHaveBeenCalledWith(
- 'fetchPage',
+ expect(eventHub.$emit).toHaveBeenCalledWith('fetchPage', [
2,
expect.any(Object),
expect.any(Object),
expect.any(Object),
- );
+ ]);
});
});
});
diff --git a/spec/frontend/groups/components/item_actions_spec.js b/spec/frontend/groups/components/item_actions_spec.js
index c0dc1a816e6..1ca42f28c7f 100644
--- a/spec/frontend/groups/components/item_actions_spec.js
+++ b/spec/frontend/groups/components/item_actions_spec.js
@@ -31,11 +31,10 @@ describe('ItemActionsComponent', () => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
vm.onLeaveGroup();
- expect(eventHub.$emit).toHaveBeenCalledWith(
- 'showLeaveGroupModal',
+ expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', [
vm.group,
vm.parentGroup,
- );
+ ]);
});
});
});
diff --git a/spec/frontend/helpers/wait_using_real_timer.js b/spec/frontend/helpers/wait_using_real_timer.js
new file mode 100644
index 00000000000..ddf23cd97b4
--- /dev/null
+++ b/spec/frontend/helpers/wait_using_real_timer.js
@@ -0,0 +1,7 @@
+/* useful for timing promises when jest fakeTimers are not reliable enough */
+export default timeout =>
+ new Promise(resolve => {
+ jest.useRealTimers();
+ setTimeout(resolve, timeout);
+ jest.useFakeTimers();
+ });
diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js
index 6187ed8cd8b..0ba58561f08 100644
--- a/spec/frontend/ide/components/repo_editor_spec.js
+++ b/spec/frontend/ide/components/repo_editor_spec.js
@@ -4,19 +4,25 @@ import MockAdapter from 'axios-mock-adapter';
import '~/behaviors/markdown/render_gfm';
import { Range } from 'monaco-editor';
import axios from '~/lib/utils/axios_utils';
+import service from '~/ide/services';
import { createStoreOptions } from '~/ide/stores';
import RepoEditor from '~/ide/components/repo_editor.vue';
import Editor from '~/ide/lib/editor';
-import { leftSidebarViews, FILE_VIEW_MODE_EDITOR, FILE_VIEW_MODE_PREVIEW } from '~/ide/constants';
+import {
+ leftSidebarViews,
+ FILE_VIEW_MODE_EDITOR,
+ FILE_VIEW_MODE_PREVIEW,
+ viewerTypes,
+} from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { file } from '../helpers';
import { exampleConfigs, exampleFiles } from '../lib/editorconfig/mock_data';
+import waitUsingRealTimer from 'helpers/wait_using_real_timer';
describe('RepoEditor', () => {
let vm;
let store;
- let mockActions;
const waitForEditorSetup = () =>
new Promise(resolve => {
@@ -30,6 +36,10 @@ describe('RepoEditor', () => {
vm = createComponentWithStore(Vue.extend(RepoEditor), store, {
file: store.state.openFiles[0],
});
+
+ jest.spyOn(vm, 'getFileData').mockResolvedValue();
+ jest.spyOn(vm, 'getRawFileData').mockResolvedValue();
+
vm.$mount();
};
@@ -43,21 +53,12 @@ describe('RepoEditor', () => {
};
beforeEach(() => {
- mockActions = {
- getFileData: jest.fn().mockResolvedValue(),
- getRawFileData: jest.fn().mockResolvedValue(),
- };
-
const f = {
...file(),
viewMode: FILE_VIEW_MODE_EDITOR,
};
const storeOptions = createStoreOptions();
- storeOptions.actions = {
- ...storeOptions.actions,
- ...mockActions,
- };
store = new Vuex.Store(storeOptions);
f.active = true;
@@ -438,7 +439,7 @@ describe('RepoEditor', () => {
vm.initEditor();
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
@@ -449,10 +450,11 @@ describe('RepoEditor', () => {
vm.file.raw = '';
vm.initEditor();
+
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).toHaveBeenCalled();
- expect(mockActions.getRawFileData).toHaveBeenCalled();
+ expect(vm.getFileData).toHaveBeenCalled();
+ expect(vm.getRawFileData).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
@@ -464,8 +466,8 @@ describe('RepoEditor', () => {
vm.initEditor();
vm.$nextTick()
.then(() => {
- expect(mockActions.getFileData).not.toHaveBeenCalled();
- expect(mockActions.getRawFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
expect(vm.editor.createInstance).not.toHaveBeenCalled();
})
.then(done)
@@ -526,6 +528,65 @@ describe('RepoEditor', () => {
});
});
+ describe('populates editor with the fetched content', () => {
+ beforeEach(() => {
+ vm.getRawFileData.mockRestore();
+ });
+
+ const createRemoteFile = name => ({
+ ...file(name),
+ tmpFile: false,
+ });
+
+ it('after switching viewer from edit to diff', async () => {
+ jest.spyOn(service, 'getRawFileData').mockImplementation(async () => {
+ expect(vm.file.loading).toBe(true);
+
+ // switching from edit to diff mode usually triggers editor initialization
+ store.state.viewer = viewerTypes.diff;
+
+ // we delay returning the file to make sure editor doesn't initialize before we fetch file content
+ await waitUsingRealTimer(10);
+ return 'rawFileData123\n';
+ });
+
+ const f = createRemoteFile('newFile');
+ Vue.set(store.state.entries, f.path, f);
+
+ vm.file = f;
+
+ // use the real timer to accurately simulate the race condition
+ await waitUsingRealTimer(20);
+ expect(vm.model.getModel().getValue()).toBe('rawFileData123\n');
+ });
+
+ it('after opening multiple files at the same time', async () => {
+ const fileA = createRemoteFile('fileA');
+ const fileB = createRemoteFile('fileB');
+ Vue.set(store.state.entries, fileA.path, fileA);
+ Vue.set(store.state.entries, fileB.path, fileB);
+
+ jest
+ .spyOn(service, 'getRawFileData')
+ .mockImplementationOnce(async () => {
+ // opening fileB while the content of fileA is still being fetched
+ vm.file = fileB;
+ return 'fileA-rawContent\n';
+ })
+ .mockImplementationOnce(async () => {
+ // we delay returning fileB content to make sure the editor doesn't initialize prematurely
+ await waitUsingRealTimer(10);
+ return 'fileB-rawContent\n';
+ });
+
+ vm.file = fileA;
+
+ // use the real timer to accurately simulate the race condition
+ await waitUsingRealTimer(20);
+ expect(vm.model.getModel().getValue()).toBe('fileB-rawContent\n');
+ });
+ });
+
describe('onPaste', () => {
const setFileName = name => {
Vue.set(vm, 'file', {
@@ -557,6 +618,11 @@ describe('RepoEditor', () => {
});
});
+ // Pasting an image does a lot of things like using the FileReader API,
+ // so, waitForPromises isn't very reliable (and causes a flaky spec)
+ // Read more about state.watch: https://vuex.vuejs.org/api/#watch
+ const waitForFileContentChange = () => watchState(s => s.entries['foo/bar.md'].content);
+
beforeEach(() => {
setFileName('bar.md');
@@ -576,13 +642,10 @@ describe('RepoEditor', () => {
});
});
- // The following test is flaky
- // see https://gitlab.com/gitlab-org/gitlab/-/issues/221039
- // eslint-disable-next-line jest/no-disabled-tests
- it.skip('adds an image entry to the same folder for a pasted image in a markdown file', () => {
+ it('adds an image entry to the same folder for a pasted image in a markdown file', () => {
pasteImage();
- return waitForPromises().then(() => {
+ return waitForFileContentChange().then(() => {
expect(vm.$store.state.entries['foo/foo.png']).toMatchObject({
path: 'foo/foo.png',
type: 'blob',
@@ -596,10 +659,7 @@ describe('RepoEditor', () => {
it("adds a markdown image tag to the file's contents", () => {
pasteImage();
- // Pasting an image does a lot of things like using the FileReader API,
- // so, waitForPromises isn't very reliable (and causes a flaky spec)
- // Read more about state.watch: https://vuex.vuejs.org/api/#watch
- return watchState(s => s.entries['foo/bar.md'].content).then(() => {
+ return waitForFileContentChange().then(() => {
expect(vm.file.content).toBe('hello world\n![foo.png](./foo.png)');
});
});
@@ -632,8 +692,8 @@ describe('RepoEditor', () => {
return waitForEditorSetup().then(() => {
expect(vm.rules).toEqual(monacoRules);
expect(vm.model.options).toMatchObject(monacoRules);
- expect(mockActions.getFileData).not.toHaveBeenCalled();
- expect(mockActions.getRawFileData).not.toHaveBeenCalled();
+ expect(vm.getFileData).not.toHaveBeenCalled();
+ expect(vm.getRawFileData).not.toHaveBeenCalled();
});
},
);
@@ -649,13 +709,13 @@ describe('RepoEditor', () => {
createComponent();
return waitForEditorSetup().then(() => {
- expect(mockActions.getFileData.mock.calls.map(([, args]) => args)).toEqual([
+ expect(vm.getFileData.mock.calls.map(([args]) => args)).toEqual([
{ makeFileActive: false, path: 'foo/bar/baz/.editorconfig' },
{ makeFileActive: false, path: 'foo/bar/.editorconfig' },
{ makeFileActive: false, path: 'foo/.editorconfig' },
{ makeFileActive: false, path: '.editorconfig' },
]);
- expect(mockActions.getRawFileData.mock.calls.map(([, args]) => args)).toEqual([
+ expect(vm.getRawFileData.mock.calls.map(([args]) => args)).toEqual([
{ path: 'foo/bar/baz/.editorconfig' },
{ path: 'foo/bar/.editorconfig' },
{ path: 'foo/.editorconfig' },
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index e2dc7626c67..827759c4901 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -446,6 +446,54 @@ describe('IDE store file actions', () => {
})
.catch(done.fail);
});
+
+ describe('sets file loading to true', () => {
+ let loadingWhenGettingRawData;
+ let loadingWhenGettingBaseRawData;
+
+ beforeEach(() => {
+ loadingWhenGettingRawData = undefined;
+ loadingWhenGettingBaseRawData = undefined;
+
+ jest.spyOn(service, 'getRawFileData').mockImplementation(f => {
+ loadingWhenGettingRawData = f.loading;
+ return Promise.resolve('raw');
+ });
+ jest.spyOn(service, 'getBaseRawFileData').mockImplementation(f => {
+ loadingWhenGettingBaseRawData = f.loading;
+ return Promise.resolve('rawBase');
+ });
+ });
+
+ it('when getting raw file data', async () => {
+ expect(tmpFile.loading).toBe(false);
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+
+ it('when getting base raw file data', async () => {
+ tmpFile.mrChange = { new_file: false };
+
+ expect(tmpFile.loading).toBe(false);
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingBaseRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+
+ it('when file was already loading', async () => {
+ tmpFile.loading = true;
+
+ await store.dispatch('getRawFileData', { path: tmpFile.path });
+
+ expect(loadingWhenGettingRawData).toBe(true);
+ expect(tmpFile.loading).toBe(false);
+ });
+ });
});
describe('return JSON', () => {
@@ -489,6 +537,12 @@ describe('IDE store file actions', () => {
});
});
});
+
+ it('toggles loading off after error', async () => {
+ await expect(store.dispatch('getRawFileData', { path: tmpFile.path })).rejects.toThrow();
+
+ expect(tmpFile.loading).toBe(false);
+ });
});
});
diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
index 8b0253dc01a..b77305277ea 100644
--- a/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_suggest_pipeline_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import { GlLink } from '@gitlab/ui';
+import { GlLink, GlSprintf } from '@gitlab/ui';
import suggestPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue';
import stubChildren from 'helpers/stub_children';
import PipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
@@ -18,6 +18,7 @@ describe('MRWidgetHeader', () => {
propsData: { pipelinePath, pipelineSvgPath, humanAccess },
stubs: {
...stubChildren(PipelineTourState),
+ GlSprintf,
},
});
});
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
index e8f95e099cc..acb7316ae27 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_pipeline_tour_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlPopover } from '@gitlab/ui';
+import { GlPopover, GlLink, GlSprintf } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
import pipelineTourState from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue';
@@ -51,6 +51,9 @@ describe('MRWidgetPipelineTour', () => {
Cookies.remove(cookieKey);
wrapper = shallowMount(pipelineTourState, {
propsData: popoverProps,
+ stubs: {
+ GlSprintf,
+ },
});
});
@@ -60,6 +63,13 @@ describe('MRWidgetPipelineTour', () => {
expect(popover.exists()).toBe(true);
});
+ it('renders the help link', () => {
+ const link = wrapper.find(GlLink);
+
+ expect(link.exists()).toBe(true);
+ expect(link.attributes('href')).toBe(wrapper.vm.$options.helpURL);
+ });
+
it('renders the show me how button', () => {
const button = findOkBtn();
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index 137d0fd4f9e..69dc10d1f55 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -226,8 +226,8 @@ describe Gitlab::BitbucketImport::Importer do
it 'counts imported pull requests' do
expect(Gitlab::Metrics).to receive(:counter).with(
- :bitbucket_importer_imported_pull_requests,
- 'The number of imported Bitbucket pull requests'
+ :bitbucket_importer_imported_merge_requests_total,
+ 'The number of imported merge (pull) requests'
)
expect(counter).to receive(:increment)
@@ -369,8 +369,8 @@ describe Gitlab::BitbucketImport::Importer do
it 'counts imported issues' do
expect(Gitlab::Metrics).to receive(:counter).with(
- :bitbucket_importer_imported_issues,
- 'The number of imported Bitbucket issues'
+ :bitbucket_importer_imported_issues_total,
+ 'The number of imported issues'
)
expect(counter).to receive(:increment)
@@ -389,23 +389,27 @@ describe Gitlab::BitbucketImport::Importer do
allow(subject).to receive(:import_issues)
allow(subject).to receive(:import_pull_requests)
- allow(Gitlab::Metrics).to receive(:counter) { counter }
- allow(Gitlab::Metrics).to receive(:histogram) { histogram }
+ allow(Gitlab::Metrics).to receive(:counter).and_return(counter)
+ allow(Gitlab::Metrics).to receive(:histogram).and_return(histogram)
+ allow(histogram).to receive(:observe)
+ allow(counter).to receive(:increment)
end
it 'counts and measures duration of imported projects' do
expect(Gitlab::Metrics).to receive(:counter).with(
- :bitbucket_importer_imported_projects,
- 'The number of imported Bitbucket projects'
+ :bitbucket_importer_imported_projects_total,
+ 'The number of imported projects'
)
expect(Gitlab::Metrics).to receive(:histogram).with(
:bitbucket_importer_total_duration_seconds,
- 'Total time spent importing Bitbucket projects, in seconds'
+ 'Total time spent importing projects, in seconds',
+ {},
+ Gitlab::Import::Metrics::IMPORT_DURATION_BUCKETS
)
expect(counter).to receive(:increment)
- expect(histogram).to receive(:observe).with({ importer: described_class::IMPORTER }, anything)
+ expect(histogram).to receive(:observe).with({ importer: :bitbucket_importer }, anything)
subject.execute
end
diff --git a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
index cf39d2cb753..570ad916fb2 100644
--- a/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_server_import/importer_spec.rb
@@ -5,7 +5,10 @@ require 'spec_helper'
describe Gitlab::BitbucketServerImport::Importer do
include ImportSpecHelper
- let(:project) { create(:project, :repository, import_url: 'http://my-bitbucket') }
+ let(:import_url) { 'http://my-bitbucket' }
+ let(:user) { 'bitbucket' }
+ let(:password) { 'test' }
+ let(:project) { create(:project, :repository, import_url: import_url) }
let(:now) { Time.now.utc.change(usec: 0) }
let(:project_key) { 'TEST' }
let(:repo_slug) { 'rouge' }
@@ -16,7 +19,7 @@ describe Gitlab::BitbucketServerImport::Importer do
before do
data = project.create_or_update_import_data(
data: { project_key: project_key, repo_slug: repo_slug },
- credentials: { base_uri: 'http://my-bitbucket', user: 'bitbucket', password: 'test' }
+ credentials: { base_uri: import_url, user: user, password: password }
)
data.save
project.save
@@ -125,6 +128,48 @@ describe Gitlab::BitbucketServerImport::Importer do
expect(note.updated_at).to eq(@pr_note.created_at)
end
+ context 'metrics' do
+ let(:histogram) { double(:histogram) }
+ let(:counter) { double('counter', increment: true) }
+
+ before do
+ allow(Gitlab::Metrics).to receive(:counter) { counter }
+ allow(Gitlab::Metrics).to receive(:histogram) { histogram }
+ allow(subject.client).to receive(:activities).and_return([@merge_event])
+ end
+
+ it 'counts and measures duration of imported projects' do
+ expect(Gitlab::Metrics).to receive(:counter).with(
+ :bitbucket_server_importer_imported_projects_total,
+ 'The number of imported projects'
+ )
+
+ expect(Gitlab::Metrics).to receive(:histogram).with(
+ :bitbucket_server_importer_total_duration_seconds,
+ 'Total time spent importing projects, in seconds',
+ {},
+ Gitlab::Import::Metrics::IMPORT_DURATION_BUCKETS
+ )
+
+ expect(counter).to receive(:increment)
+ expect(histogram).to receive(:observe).with({ importer: :bitbucket_server_importer }, anything)
+
+ subject.execute
+ end
+
+ it 'counts imported pull requests' do
+ expect(Gitlab::Metrics).to receive(:counter).with(
+ :bitbucket_server_importer_imported_merge_requests_total,
+ 'The number of imported merge (pull) requests'
+ )
+
+ expect(counter).to receive(:increment)
+ allow(histogram).to receive(:observe).with({ importer: :bitbucket_server_importer }, anything)
+
+ subject.execute
+ end
+ end
+
it 'imports threaded discussions' do
reply = instance_double(
BitbucketServer::Representation::PullRequestComment,
diff --git a/spec/lib/gitlab/import/metrics_spec.rb b/spec/lib/gitlab/import/metrics_spec.rb
index 0799d19fcef..89ea4db5c6e 100644
--- a/spec/lib/gitlab/import/metrics_spec.rb
+++ b/spec/lib/gitlab/import/metrics_spec.rb
@@ -3,54 +3,38 @@
require 'spec_helper'
describe Gitlab::Import::Metrics do
- let(:importer_stub) do
- Class.new do
- prepend Gitlab::Import::Metrics
-
- Gitlab::Import::Metrics.measure :execute, metrics: {
- importer_counter: {
- type: :counter,
- description: 'description'
- },
- importer_histogram: {
- type: :histogram,
- labels: { importer: 'importer' },
- description: 'description'
- }
- }
-
- def execute
- true
- end
+ let(:importer) { :test_importer }
+ let(:project) { create(:project) }
+ let(:histogram) { double(:histogram) }
+ let(:counter) { double(:counter) }
+
+ subject { described_class.new(importer, project) }
+
+ describe '#report_import_time' do
+ before do
+ allow(Gitlab::Metrics).to receive(:counter) { counter }
+ allow(Gitlab::Metrics).to receive(:histogram) { histogram }
+ allow(counter).to receive(:increment)
+ allow(counter).to receive(:observe)
end
- end
-
- subject { importer_stub.new.execute }
- describe '#execute' do
- let(:counter) { double(:counter) }
- let(:histogram) { double(:histogram) }
+ it 'emits importer metrics' do
+ expect(Gitlab::Metrics).to receive(:counter).with(
+ :test_importer_imported_projects_total,
+ 'The number of imported projects'
+ )
- it 'increments counter metric' do
- expect(Gitlab::Metrics)
- .to receive(:counter)
- .with(:importer_counter, 'description')
- .and_return(counter)
+ expect(Gitlab::Metrics).to receive(:histogram).with(
+ :test_importer_total_duration_seconds,
+ 'Total time spent importing projects, in seconds',
+ {},
+ described_class::IMPORT_DURATION_BUCKETS
+ )
expect(counter).to receive(:increment)
+ expect(histogram).to receive(:observe).with({ importer: :test_importer }, anything)
- subject
- end
-
- it 'measures method duration and reports histogram metric' do
- expect(Gitlab::Metrics)
- .to receive(:histogram)
- .with(:importer_histogram, 'description')
- .and_return(histogram)
-
- expect(histogram).to receive(:observe).with({ importer: 'importer' }, anything)
-
- subject
+ subject.track_finished_import
end
end
end
diff --git a/yarn.lock b/yarn.lock
index 902ee0246ce..6b482aa33a2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1138,20 +1138,20 @@
dependencies:
defer-to-connect "^1.0.1"
-"@toast-ui/editor@2.1.2", "@toast-ui/editor@^2.1.2":
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/@toast-ui/editor/-/editor-2.1.2.tgz#0472431bd039ae70882d77910e83f0ad222d0b1c"
- integrity sha512-yoWRVyp2m1dODH+bmzJaILUgl2L57GCQJ8c8+XRgJMwfxb/TFz5U+oT8JGAU5VwozIzKF0SyVMs8AEePwwhIIA==
+"@toast-ui/editor@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@toast-ui/editor/-/editor-2.2.0.tgz#77fd790c6ae876d5de738bc022d6ebc5c84a6feb"
+ integrity sha512-WiqrY7OeCOS08NlznJobCwtxOWJC/5my8QefHCKTZyX9/70kkojcnyQ8aoiQQ5kIfGUJ6dKt6/JuKD5OOib+bQ==
dependencies:
"@types/codemirror" "0.0.71"
codemirror "^5.48.4"
-"@toast-ui/vue-editor@2.1.2":
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/@toast-ui/vue-editor/-/vue-editor-2.1.2.tgz#a790e69fcf7fb426e6b8ea190733477c3cc756aa"
- integrity sha512-RK01W6D8FqtNq4MjWsXk6KRzOU/vL6mpiADAnH5l/lFK4G6UQJhLKsMRfmxIqCH+ivm8VtQzGdd9obUfD+XbCw==
+"@toast-ui/vue-editor@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@toast-ui/vue-editor/-/vue-editor-2.2.0.tgz#bae8e6e6c0a7d6fb40a4f6b8e616aada3923118d"
+ integrity sha512-z8q60tEIfrIOk1fQitRg56ZxztOUyp2A1gLlTVuTpFNts21lTsMfFcUNdZsAivWUN6ToQu4qP8Bz80h9FZBLBg==
dependencies:
- "@toast-ui/editor" "^2.1.2"
+ "@toast-ui/editor" "^2.2.0"
"@types/anymatch@*":
version "1.3.0"