diff --git a/Gemfile.lock b/Gemfile.lock index a53485e6220c6c37cd64c74607d05e8f2ad58e46..7582eb28e492e437204e5861c48c3d65e65ea81b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,8 +50,8 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - acts-as-taggable-on (6.0.0) - activerecord (~> 5.0) + acts-as-taggable-on (6.5.0) + activerecord (>= 5.0, < 6.1) adamantium (0.2.0) ice_nine (~> 0.11.0) memoizable (~> 0.4.0) diff --git a/app/assets/javascripts/behaviors/preview_markdown.js b/app/assets/javascripts/behaviors/preview_markdown.js index a07942d87cb511994ddc5b53a8c2b094b401aba4..ca91400eac70471e98f9a779c820d0ebe6cf85f5 100644 --- a/app/assets/javascripts/behaviors/preview_markdown.js +++ b/app/assets/javascripts/behaviors/preview_markdown.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var */ +/* eslint-disable func-names */ import $ from 'jquery'; import axios from '~/lib/utils/axios_utils'; @@ -12,11 +12,8 @@ import { __ } from '~/locale'; // more than `x` users are referenced. // -var lastTextareaPreviewed; -var lastTextareaHeight = null; -var markdownPreview; -var previewButtonSelector; -var writeButtonSelector; +let lastTextareaHeight; +let lastTextareaPreviewed; function MarkdownPreview() {} @@ -27,14 +24,13 @@ MarkdownPreview.prototype.emptyMessage = __('Nothing to preview.'); MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.showPreview = function($form) { - var mdText; - var preview = $form.find('.js-md-preview'); - var url = preview.data('url'); + const preview = $form.find('.js-md-preview'); + const url = preview.data('url'); if (preview.hasClass('md-preview-loading')) { return; } - mdText = $form.find('textarea.markdown-area').val(); + const mdText = $form.find('textarea.markdown-area').val(); if (mdText === undefined) { return; @@ -46,7 +42,7 @@ MarkdownPreview.prototype.showPreview = function($form) { } else { preview.addClass('md-preview-loading').text(__('Loading...')); this.fetchMarkdownPreview(mdText, url, response => { - var body; + let body; if (response.body.length > 0) { ({ body } = response); } else { @@ -91,8 +87,7 @@ MarkdownPreview.prototype.hideReferencedUsers = function($form) { }; MarkdownPreview.prototype.renderReferencedUsers = function(users, $form) { - var referencedUsers; - referencedUsers = $form.find('.referenced-users'); + const referencedUsers = $form.find('.referenced-users'); if (referencedUsers.length) { if (users.length >= this.referenceThreshold) { referencedUsers.show(); @@ -108,8 +103,7 @@ MarkdownPreview.prototype.hideReferencedCommands = function($form) { }; MarkdownPreview.prototype.renderReferencedCommands = function(commands, $form) { - var referencedCommands; - referencedCommands = $form.find('.referenced-commands'); + const referencedCommands = $form.find('.referenced-commands'); if (commands.length > 0) { referencedCommands.html(commands); referencedCommands.show(); @@ -119,15 +113,15 @@ MarkdownPreview.prototype.renderReferencedCommands = function(commands, $form) { } }; -markdownPreview = new MarkdownPreview(); +const markdownPreview = new MarkdownPreview(); -previewButtonSelector = '.js-md-preview-button'; -writeButtonSelector = '.js-md-write-button'; +const previewButtonSelector = '.js-md-preview-button'; +const writeButtonSelector = '.js-md-write-button'; lastTextareaPreviewed = null; const markdownToolbar = $('.md-header-toolbar'); $.fn.setupMarkdownPreview = function() { - var $form = $(this); + const $form = $(this); $form.find('textarea.markdown-area').on('input', () => { markdownPreview.hideReferencedUsers($form); }); @@ -188,7 +182,7 @@ $(document).on('markdown-preview:hide', (e, $form) => { }); $(document).on('markdown-preview:toggle', (e, keyboardEvent) => { - var $target; + let $target; $target = $(keyboardEvent.target); if ($target.is('textarea.markdown-area')) { $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]); @@ -201,16 +195,14 @@ $(document).on('markdown-preview:toggle', (e, keyboardEvent) => { }); $(document).on('click', previewButtonSelector, function(e) { - var $form; e.preventDefault(); - $form = $(this).closest('form'); + const $form = $(this).closest('form'); $(document).triggerHandler('markdown-preview:show', [$form]); }); $(document).on('click', writeButtonSelector, function(e) { - var $form; e.preventDefault(); - $form = $(this).closest('form'); + const $form = $(this).closest('form'); $(document).triggerHandler('markdown-preview:hide', [$form]); }); diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 81ba15577fbe7bb378446bd86be57a470b634e20..a23707209dcb776e24602fe3f8771a01818ff19c 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, one-var, no-var, no-else-return */ +/* eslint-disable func-names, no-else-return */ import $ from 'jquery'; import { __ } from './locale'; @@ -8,9 +8,8 @@ import { capitalizeFirstCharacter } from './lib/utils/text_utility'; export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) { $('.js-compare-dropdown').each(function() { - var $dropdown, selected; - $dropdown = $(this); - selected = $dropdown.data('selected'); + const $dropdown = $(this); + const selected = $dropdown.data('selected'); const $dropdownContainer = $dropdown.closest('.dropdown'); const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer); @@ -44,17 +43,16 @@ export default function initCompareAutocomplete(limitTo = null, clickHandler = ( fieldName: $dropdown.data('fieldName'), filterInput: 'input[type="search"]', renderRow(ref) { - var link; + const link = $('') + .attr('href', '#') + .addClass(ref === selected ? 'is-active' : '') + .text(ref) + .attr('data-ref', ref); if (ref.header != null) { return $('
') .addClass('dropdown-header') .text(ref.header); } else { - link = $('') - .attr('href', '#') - .addClass(ref === selected ? 'is-active' : '') - .text(ref) - .attr('data-ref', ref); return $('').append(link); } }, diff --git a/app/assets/javascripts/new_commit_form.js b/app/assets/javascripts/new_commit_form.js index b142f212eb0cbaa41be36c13582784174b55ab6e..037be8467cb947d88864515fb8aed2ec7e2f9ca1 100644 --- a/app/assets/javascripts/new_commit_form.js +++ b/app/assets/javascripts/new_commit_form.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, no-return-assign */ +/* eslint-disable no-return-assign */ export default class NewCommitForm { constructor(form) { this.form = form; @@ -11,8 +11,7 @@ export default class NewCommitForm { this.renderDestination(); } renderDestination() { - var different; - different = this.branchName.val() !== this.originalBranch.val(); + const different = this.branchName.val() !== this.originalBranch.val(); if (different) { this.createMergeRequestContainer.show(); if (!this.wasDifferent) { diff --git a/changelogs/unreleased/remove_var_from_new_commit_form_js.yml b/changelogs/unreleased/remove_var_from_new_commit_form_js.yml new file mode 100644 index 0000000000000000000000000000000000000000..0abac1eb7c9b531619c4d5ad57388580b9dd1589 --- /dev/null +++ b/changelogs/unreleased/remove_var_from_new_commit_form_js.yml @@ -0,0 +1,5 @@ +--- +title: Remove var from new_commit_form.js +merge_request: 20095 +author: Lee Tickett +type: other diff --git a/changelogs/unreleased/remove_var_from_preview_markdown_js.yml b/changelogs/unreleased/remove_var_from_preview_markdown_js.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea85fb8f2750ca95999295f79b350c5d962eeaaa --- /dev/null +++ b/changelogs/unreleased/remove_var_from_preview_markdown_js.yml @@ -0,0 +1,5 @@ +--- +title: Remove var from preview_markdown.js +merge_request: 20115 +author: Lee Tickett +type: other diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index d7cfa0b6c16e703d0ae6fc5fb16968c01ec797b9..822836500708ce4c29ec4a06c567c238b8c6d10f 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -559,6 +559,9 @@ a few things that you need to do: including [incremental logging](../job_logs.md#new-incremental-logging-architecture). 1. Configure [object storage for LFS objects](../lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage). 1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only). +1. Configure [object storage for Merge Request Diffs](../merge_request_diffs.md#using-object-storage). +1. Configure [object storage for Packages](../packages/index.md#using-object-storage) (Optional Feature). +1. Configure [object storage for Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (Optional Feature). NOTE: **Note:** One current feature of GitLab that still requires a shared directory (NFS) is diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index 199944a160c7d3d42b5dd6ace4604702f1d1d673..c9c9b469a8982e65eed4cd590cbfb0938d021e23 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -38,14 +38,17 @@ The following components need to be considered for a scaled or highly-available environment. In many cases, components can be combined on the same nodes to reduce complexity. -- Unicorn/Workhorse - Web-requests (UI, API, Git over HTTP) +- GitLab application nodes (Unicorn / Puma, Workhorse) - Web-requests (UI, API, Git over HTTP) - Sidekiq - Asynchronous/Background jobs - PostgreSQL - Database - Consul - Database service discovery and health checks/failover - PgBouncer - Database pool manager - Redis - Key/Value store (User sessions, cache, queue for Sidekiq) - Sentinel - Redis health check/failover manager -- Gitaly - Provides high-level RPC access to Git repositories +- Gitaly - Provides high-level storage and RPC access to Git repositories +- S3 Object Storage service[^3] and / or NFS storage servers[^4] for entities such as Uploads, Artifacts, LFS Objects, etc... +- Load Balancer[^2] - Main entry point and handles load balancing for the GitLab application nodes. +- Monitor - Prometheus and Grafana monitoring with auto discovery. ## Scalable Architecture Examples @@ -67,8 +70,10 @@ larger one. - 1 PostgreSQL node - 1 Redis node -- 1 NFS/Gitaly storage server -- 2 or more GitLab application nodes (Unicorn, Workhorse, Sidekiq) +- 1 Gitaly node +- 1 or more Object Storage services[^3] and / or NFS storage server[^4] +- 2 or more GitLab application nodes (Unicorn / Puma, Workhorse, Sidekiq) +- 1 or more Load Balancer nodes[^2] - 1 Monitoring node (Prometheus, Grafana) #### Installation Instructions @@ -79,8 +84,10 @@ you can continue with the next step. 1. [PostgreSQL](database.md#postgresql-in-a-scaled-environment) 1. [Redis](redis.md#redis-in-a-scaled-environment) -1. [Gitaly](gitaly.md) (recommended) or [NFS](nfs.md) +1. [Gitaly](gitaly.md) (recommended) and / or [NFS](nfs.md)[^4] 1. [GitLab application nodes](gitlab.md) + - With [Object Storage service enabled](../gitaly/index.md#eliminating-nfs-altogether)[^3] +1. [Load Balancer](load_balancer.md)[^2] 1. [Monitoring node (Prometheus and Grafana)](monitoring_node.md) ### Full Scaling @@ -91,11 +98,13 @@ is split into separate Sidekiq and Unicorn/Workhorse nodes. One indication that this architecture is required is if Sidekiq queues begin to periodically increase in size, indicating that there is contention or there are not enough resources. -- 1 PostgreSQL node -- 1 Redis node -- 2 or more NFS/Gitaly storage servers +- 1 or more PostgreSQL node +- 1 or more Redis node +- 1 or more Gitaly storage servers +- 1 or more Object Storage services[^3] and / or NFS storage server[^4] - 2 or more Sidekiq nodes -- 2 or more GitLab application nodes (Unicorn, Workhorse) +- 2 or more GitLab application nodes (Unicorn / Puma, Workhorse, Sidekiq) +- 1 or more Load Balancer nodes[^2] - 1 Monitoring node (Prometheus, Grafana) ## High Availability Architecture Examples @@ -114,10 +123,10 @@ This may lead to the other nodes believing a failure has occurred and initiating automated failover. Isolating Redis and Consul from the services they monitor reduces the chances of a false positive that a failure has occurred. -The examples below do not really address high availability of NFS. Some enterprises -have access to NFS appliances that manage availability. This is the best case -scenario. In the future, GitLab may offer a more user-friendly solution to -[GitLab HA Storage](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2472). +The examples below do not address high availability of NFS for objects. We recommend a +S3 Object Storage service[^3] is used where possible over NFS but it's still required in +certain cases[^4]. Where NFS is to be used some enterprises have access to NFS appliances +that manage availability and this would be best case scenario. There are many options in between each of these examples. Work with GitLab Support to understand the best starting point for your workload and adapt from there. @@ -138,8 +147,10 @@ the contention. - 3 PostgreSQL nodes - 2 Redis nodes - 3 Consul/Sentinel nodes -- 2 or more GitLab application nodes (Unicorn, Workhorse, Sidekiq, PgBouncer) -- 1 NFS/Gitaly server +- 2 or more GitLab application nodes (Unicorn / Puma, Workhorse, Sidekiq) +- 1 Gitaly storage servers +- 1 Object Storage service[^3] and / or NFS storage server[^4] +- 1 or more Load Balancer nodes[^2] - 1 Monitoring node (Prometheus, Grafana) ![Horizontal architecture diagram](img/horizontal.png) @@ -156,8 +167,10 @@ contention due to certain workloads. - 2 Redis nodes - 3 Consul/Sentinel nodes - 2 or more Sidekiq nodes -- 2 or more GitLab application nodes (Unicorn, Workhorse) -- 1 or more NFS/Gitaly servers +- 2 or more GitLab application nodes (Unicorn / Puma, Workhorse, Sidekiq) +- 1 Gitaly storage servers +- 1 Object Storage service[^3] and / or NFS storage server[^4] +- 1 or more Load Balancer nodes[^2] - 1 Monitoring node (Prometheus, Grafana) ![Hybrid architecture diagram](img/hybrid.png) @@ -177,45 +190,40 @@ with the added complexity of many more nodes to configure, manage, and monitor. - 2 or more Git nodes (Git over SSH/Git over HTTP) - 2 or more API nodes (All requests to `/api`) - 2 or more Web nodes (All other web requests) -- 2 or more NFS/Gitaly servers +- 2 or more Gitaly storage servers +- 1 or more Object Storage services[^3] and / or NFS storage servers[^4] +- 1 or more Load Balancer nodes[^2] - 1 Monitoring node (Prometheus, Grafana) ![Fully Distributed architecture diagram](img/fully-distributed.png) -The following pages outline the steps necessary to configure each component -separately: +## Reference Architecture Examples -1. [Configure the database](database.md) -1. [Configure Redis](redis.md) - 1. [Configure Redis for GitLab source installations](redis_source.md) -1. [Configure NFS](nfs.md) - 1. [NFS Client and Host setup](nfs_host_client_setup.md) -1. [Configure the GitLab application servers](gitlab.md) -1. [Configure the load balancers](load_balancer.md) -1. [Monitoring node (Prometheus and Grafana)](monitoring_node.md) +The Support and Quality teams build, performance test, and validate Reference +Architectures that support set large numbers of users. The specifications below are a +representation of this work so far and may be adjusted in the future based on +additional testing and iteration. -## Reference Architecture Examples +The architectures have been tested with specific coded workloads. The throughputs +used for testing are calculated based on sample customer data. We test each endpoint +type with the following number of requests per second (RPS) per 1000 users: -These reference architecture examples rely on the general rule that approximately 2 requests per second (RPS) of load is generated for every 100 users. +- API: 20 RPS +- Web: 2 RPS +- Git: 2 RPS -The specifications here were performance tested against a specific coded -workload. Your exact needs may be more, depending on your workload. Your +Note that your exact needs may be more, depending on your workload. Your workload is influenced by factors such as - but not limited to - how active your users are, how much automation you use, mirroring, and repo/change size. ### 10,000 User Configuration - **Supported Users (approximate):** 10,000 -- **RPS:** 200 requests per second +- **Test RPS Rates:** API: 200 RPS, Web: 20 RPS, Git: 20 RPS - **Known Issues:** While validating the reference architecture, slow API endpoints were discovered. For details, see the related issues list in [this issue](https://gitlab.com/gitlab-org/gitlab-foss/issues/64335). -The Support and Quality teams built, performance tested, and validated an -environment that supports about 10,000 users. The specifications below are a -representation of the work so far. The specifications may be adjusted in the -future based on additional testing and iteration. - | Service | Configuration | GCP type | | ------------------------------|-------------------------|----------------| | 3 GitLab Rails