diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-18 18:10:08 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-18 18:10:08 +0000 |
| commit | bc578c5f89ff9d8ec03fbbd014714f9d1e5cb172 (patch) | |
| tree | 50860880bb4f245730484e1538433c6667bbbf7b | |
| parent | 07268180a2783e58115f872ae8ba6c95e80d79a9 (diff) | |
| download | gitlab-ce-bc578c5f89ff9d8ec03fbbd014714f9d1e5cb172.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
30 files changed, 656 insertions, 220 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 1af466b7274..fa3562cbcfe 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -633a58b478d6fcffc59fe600fe8dcbd66bec42f1 +eda57b4bf0dc87dee85745d48108523f34b8f8b5 diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 7761ffaac84..3b408de5f01 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -3,7 +3,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController before_action :project, only: [:create] - feature_category :continuous_integration + feature_category :runner def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 66816d4c587..2796760fbe1 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -206,8 +206,6 @@ class GroupsController < Groups::ApplicationController protected def render_show_html - record_experiment_user(:invite_members_empty_group_version_a) if ::Gitlab.com? - render 'groups/show', locals: { trial: params[:trial] } end diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index fa6adc9431d..5da81045e02 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -5,7 +5,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController layout 'project_settings' - feature_category :continuous_integration + feature_category :runner def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/runner_setup_controller.rb b/app/controllers/runner_setup_controller.rb index e0e9c5b7c23..89b635d5a6f 100644 --- a/app/controllers/runner_setup_controller.rb +++ b/app/controllers/runner_setup_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class RunnerSetupController < ApplicationController - feature_category :continuous_integration + feature_category :runner def platforms render json: Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS) diff --git a/app/helpers/invite_members_helper.rb b/app/helpers/invite_members_helper.rb index e9e5f8b3a0b..52c05bdce2f 100644 --- a/app/helpers/invite_members_helper.rb +++ b/app/helpers/invite_members_helper.rb @@ -17,10 +17,6 @@ module InviteMembersHelper end end - def invite_group_members?(group) - experiment_enabled?(:invite_members_empty_group_version_a) && Ability.allowed?(current_user, :admin_group_member, group) - end - def invite_accepted_notice(member) case member.source when Project diff --git a/app/views/shared/groups/_empty_state.html.haml b/app/views/shared/groups/_empty_state.html.haml index 506954c53ca..aaba9697fea 100644 --- a/app/views/shared/groups/_empty_state.html.haml +++ b/app/views/shared/groups/_empty_state.html.haml @@ -6,8 +6,3 @@ %h4= s_("GroupsEmptyState|A group is a collection of several projects.") %p= s_("GroupsEmptyState|If you organize your projects under a group, it works like a folder.") %p= s_("GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group.") - - if invite_group_members?(@group) - = link_to _('Invite your team'), - group_group_members_path(@group), - class: 'gl-button btn btn-confirm-secondary', - data: { track_event: 'click_invite_team_group_empty_state', track_label: 'invite_team_group_empty_state' } diff --git a/config/feature_flags/experiment/invite_members_empty_group_version_a_experiment_percentage.yml b/config/feature_flags/experiment/invite_members_empty_group_version_a_experiment_percentage.yml deleted file mode 100644 index 6f8186a6888..00000000000 --- a/config/feature_flags/experiment/invite_members_empty_group_version_a_experiment_percentage.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: invite_members_empty_group_version_a_experiment_percentage -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45689 -rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/280 -milestone: '13.6' -type: experiment -group: group::expansion -default_enabled: false diff --git a/db/migrate/20210818034001_index_historical_data_on_recorded_at.rb b/db/migrate/20210818034001_index_historical_data_on_recorded_at.rb new file mode 100644 index 00000000000..791c5b60e54 --- /dev/null +++ b/db/migrate/20210818034001_index_historical_data_on_recorded_at.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class IndexHistoricalDataOnRecordedAt < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + INDEX_NAME = 'index_historical_data_on_recorded_at' + + disable_ddl_transaction! + + def up + add_concurrent_index :historical_data, :recorded_at, name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :historical_data, INDEX_NAME + end +end diff --git a/db/schema_migrations/20210818034001 b/db/schema_migrations/20210818034001 new file mode 100644 index 00000000000..e20a891b5f1 --- /dev/null +++ b/db/schema_migrations/20210818034001 @@ -0,0 +1 @@ +ebf0c2a7b0563dafa562136a0758baf631db92c0ce33f7200da24092f1eb2930
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 22e89176ce3..d6474efffc5 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -24135,6 +24135,8 @@ CREATE UNIQUE INDEX index_group_wiki_repositories_on_disk_path ON group_wiki_rep CREATE INDEX index_group_wiki_repositories_on_shard_id ON group_wiki_repositories USING btree (shard_id); +CREATE INDEX index_historical_data_on_recorded_at ON historical_data USING btree (recorded_at); + CREATE UNIQUE INDEX index_http_integrations_on_active_and_project_and_endpoint ON alert_management_http_integrations USING btree (active, project_id, endpoint_identifier) WHERE active; CREATE INDEX index_identities_on_saml_provider_id ON identities USING btree (saml_provider_id) WHERE (saml_provider_id IS NOT NULL); diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md index 79977ab7e3a..1ea2ec4c904 100644 --- a/doc/administration/instance_limits.md +++ b/doc/administration/instance_limits.md @@ -230,8 +230,10 @@ Set the limit to `0` to disable it. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/237891) in GitLab 13.7. -The [minimum time between pull refreshes](../user/project/repository/repository_mirroring.md) -defaults to 300 seconds (5 minutes). +The [minimum wait time between pull refreshes](../user/project/repository/repository_mirroring.md) +defaults to 300 seconds (5 minutes). For example, by default a pull refresh will only run once in a given 300 second period regardless of how many times you try to trigger it. + +This setting applies in the context of pull refreshes invoked via the [projects API](../api/projects.md#start-the-pull-mirroring-process-for-a-project), or when forcing an update by selecting the **Update now** (**{retry}**) button within **Settings > Repository > Mirroring repositories**. This setting has no effect on the automatic 30 minute interval schedule used by Sidekiq for [pull mirroring](../user/project/repository/repository_mirroring.md#how-it-works). To change this limit for a self-managed installation, run the following in the [GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session): diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md index 86f48a23cd0..b5c3330b2ce 100644 --- a/doc/administration/pages/source.md +++ b/doc/administration/pages/source.md @@ -127,17 +127,43 @@ The Pages daemon doesn't listen to the outside world. # path: shared/pages host: example.io - port: 80 + access_control: false + port: 8090 https: false + artifacts_server: false + external_http: ["127.0.0.1:8090"] + secret_file: /home/git/gitlab/gitlab-pages/gitlab-pages-secret + ``` + +1. Add the following configuration file to + `/home/git/gitlab/gitlab-pages/gitlab-pages.conf`, and be sure to change + `example.io` to the FQDN from which you want to serve GitLab Pages and + `gitlab.example.com` to the URL of your GitLab instance: + + ```ini + listen-http=:8090 + pages-root=/home/git/gitlab/shared/pages + api-secret-key=/home/git/gitlab/gitlab-pages-secret + pages-domain=example.io + internal-gitlab-server=https://gitlab.example.com + ``` + + You may use an `http` address, when running GitLab Pages and GitLab on the + same host. If you use `https` and use a self-signed certificate, be sure to + make your custom CA available to GitLab Pages, for example by setting the + `SSL_CERT_DIR` environment variable. + +1. Add the secret API key: + + ```shell + sudo -u git -H openssl rand -base64 32 > /home/git/gitlab/gitlab-pages-secret ``` 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in - order to enable the pages daemon. In `gitlab_pages_options` the - `-pages-domain` must match the `host` setting that you set above. + order to enable the pages daemon: ```ini gitlab_pages_enabled=true - gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090" ``` 1. Copy the `gitlab-pages` NGINX configuration file: diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index b6f028e1326..1308697c16e 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -57,7 +57,7 @@ consists of: ### Consul server node -The Consul server node runs the Consul server service. These nodes must have reached the quorum and elected a leader _before_ Patroni cluster bootstrap otherwise database nodes will wait until such Consul leader is elected. +The Consul server node runs the Consul server service. These nodes must have reached the quorum and elected a leader _before_ Patroni cluster bootstrap otherwise database nodes wait until such Consul leader is elected. ### PgBouncer node @@ -67,7 +67,7 @@ Each PgBouncer node runs two services: `Consul` agent - Watches the status of the PostgreSQL service definition on the Consul cluster. If that status changes, Consul runs a script which updates the -PgBouncer configuration to point to the new PostgreSQL master node and reloads +PgBouncer configuration to point to the new PostgreSQL leader node and reloads the PgBouncer service. ### Connection flow @@ -84,7 +84,7 @@ Each service in the package comes with a set of [default ports](https://docs.git ### Required information -Before proceeding with configuration, you will need to collect all the necessary +Before proceeding with configuration, you need to collect all the necessary information. #### Network information @@ -93,12 +93,12 @@ PostgreSQL doesn't listen on any network interface by default. It needs to know which IP address to listen on to be accessible to other services. Similarly, PostgreSQL access is controlled based on the network source. -This is why you will need: +This is why you need: -- IP address of each nodes network interface. This can be set to `0.0.0.0` to +- The IP address of each node's network interface. This can be set to `0.0.0.0` to listen on all interfaces. It cannot be set to the loopback address `127.0.0.1`. - Network Address. This can be in subnet (that is, `192.168.0.0/255.255.255.0`) - or CIDR (that is, `192.168.0.0/24`) form. + or Classless Inter-Domain Routing (CIDR) (`192.168.0.0/24`) form. #### Consul information @@ -141,12 +141,12 @@ patroni['postgresql']['max_replication_slots'] = 6 patroni['postgresql']['max_wal_senders'] = 7 ``` -As previously mentioned, you'll have to prepare the network subnets that will -be allowed to authenticate with the database. -You'll also need to supply the IP addresses or DNS records of Consul -server nodes. +As previously mentioned, prepare the network subnets that need permission +to authenticate with the database. +You also need to have the IP addresses or DNS records of Consul +server nodes on hand. -We will need the following password information for the application's database user: +You need the following password information for the application's database user: - `POSTGRESQL_USERNAME`. The default user for Omnibus GitLab is `gitlab` - `POSTGRESQL_USER_PASSWORD`. The password for the database user @@ -159,14 +159,14 @@ We will need the following password information for the application's database u #### Patroni information -We will need the following password information for the Patroni API: +You need the following password information for the Patroni API: - `PATRONI_API_USERNAME`. A username for basic auth to the API - `PATRONI_API_PASSWORD`. A password for basic auth to the API #### PgBouncer information -When using default setup, minimum configuration requires: +When using a default setup, the minimum configuration requires: - `PGBOUNCER_USERNAME`. The default user for Omnibus GitLab is `pgbouncer` - `PGBOUNCER_PASSWORD`. This is a password for PgBouncer service. @@ -179,11 +179,11 @@ When using default setup, minimum configuration requires: - `PGBOUNCER_NODE`, is the IP address or a FQDN of the node running PgBouncer. -Few notes on the service itself: +Few things to remember about the service itself: - The service runs as the same system account as the database - In the package, this is by default `gitlab-psql` -- If you use a non-default user account for PgBouncer service (by default `pgbouncer`), you will have to specify this username. We will refer to this requirement with `PGBOUNCER_USERNAME`. +- If you use a non-default user account for PgBouncer service (by default `pgbouncer`), you need to specify this username. - Passwords are stored in the following locations: - `/etc/gitlab/gitlab.rb`: hashed, and in plain text - `/var/opt/gitlab/pgbouncer/pg_auth`: hashed @@ -207,7 +207,7 @@ When installing the GitLab package, do not supply `EXTERNAL_URL` value. You must enable Patroni explicitly to be able to use it (with `patroni['enable'] = true`). Any PostgreSQL configuration item that controls replication, for example `wal_level`, `max_wal_senders`, etc, are strictly -controlled by Patroni and will override the original settings that you make with the `postgresql[...]` configuration key. +controlled by Patroni. These configurations override the original settings that you make with the `postgresql[...]` configuration key. Hence, they are all separated and placed under `patroni['postgresql'][...]`. This behavior is limited to replication. Patroni honours any other PostgreSQL configuration that was made with the `postgresql[...]` configuration key. For example, `max_wal_senders` by default is set to `5`. If you wish to change this you must set it with the `patroni['postgresql']['max_wal_senders']` @@ -277,12 +277,12 @@ consul['configuration'] = { All database nodes use the same configuration. The leader node is not determined in configuration, and there is no additional or different configuration for either leader or replica nodes. -Once the configuration of a node is done, you must [reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) +After the configuration of a node is complete, you must [reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) on each node for the changes to take effect. Generally, when Consul cluster is ready, the first node that [reconfigures](../restart_gitlab.md#omnibus-gitlab-reconfigure) becomes the leader. You do not need to sequence the nodes reconfiguration. You can run them in parallel or in any order. -If you choose an arbitrary order you do not have any predetermined master. +If you choose an arbitrary order you do not have any predetermined leader. #### Enable Monitoring @@ -415,19 +415,19 @@ authentication mode (`patroni['tls_client_mode']`), must each have the same valu #### PgBouncer Checkpoint -1. Ensure each node is talking to the current master: +1. Ensure each node is talking to the current node leader: ```shell gitlab-ctl pgb-console # Supply PGBOUNCER_PASSWORD when prompted ``` If there is an error `psql: ERROR: Auth failed` after typing in the - password, ensure you previously generated the MD5 password hashes with the correct + password, ensure you have previously generated the MD5 password hashes with the correct format. The correct format is to concatenate the password and the username: `PASSWORDUSERNAME`. For example, `Sup3rS3cr3tpgbouncer` would be the text needed to generate an MD5 password hash for the `pgbouncer` user. -1. Once the console prompt is available, run the following queries: +1. After the console prompt has become available, run the following queries: ```shell show databases ; show clients ; @@ -450,7 +450,7 @@ authentication mode (`patroni['tls_client_mode']`), must each have the same valu #### Configure the internal load balancer -If you're running more than one PgBouncer node as recommended, then at this time you'll need to set up a TCP internal load balancer to serve each correctly. This can be done with any reputable TCP load balancer. +If you're running more than one PgBouncer node as recommended, then you need to set up a TCP internal load balancer to serve each correctly. This can be accomplished with any reputable TCP load balancer. As an example here's how you could do it with [HAProxy](https://www.haproxy.org/): @@ -510,8 +510,7 @@ Ensure that all migrations ran: gitlab-rake gitlab:db:configure ``` -> **Note**: If you encounter a `rake aborted!` error stating that PgBouncer is failing to connect to -PostgreSQL it may be that your PgBouncer node's IP address is missing from +> **Note**: If you encounter a `rake aborted!` error stating that PgBouncer is failing to connect to PostgreSQL it may be that your PgBouncer node's IP address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb` on your database nodes. See [PgBouncer error `ERROR: pgbouncer cannot connect to server`](#pgbouncer-error-error-pgbouncer-cannot-connect-to-server) in the Troubleshooting section before proceeding. @@ -557,7 +556,7 @@ Here is a list and description of each machine and the assigned IP: All passwords are set to `toomanysecrets`, please do not use this password or derived hashes and the `external_url` for GitLab is `http://gitlab.example.com`. -After the initial configuration, if a failover occurs, the PostgresSQL master will change to one of the available secondaries until it is failed back. +After the initial configuration, if a failover occurs, the PostgresSQL leader node changes to one of the available secondaries until it is failed back. #### Example recommended setup for Consul servers @@ -689,7 +688,7 @@ All passwords are set to `toomanysecrets`, please do not use this password or de The `external_url` for GitLab is `http://gitlab.example.com` -After the initial configuration, if a failover occurs, the PostgresSQL master will change to one of the available secondaries until it is failed back. +After the initial configuration, if a failover occurs, the PostgresSQL leader node changes to one of the available secondaries until it is failed back. #### Example minimal configuration for database servers @@ -783,21 +782,14 @@ The manual steps for this configuration are the same as for the [example recomme NOTE: Using Patroni instead of Repmgr is supported for PostgreSQL 11 and required for PostgreSQL 12. Starting with GitLab 14.0, only PostgreSQL 12 is available and hence Patroni is mandatory to achieve failover and replication. -Patroni is an opinionated solution for PostgreSQL high-availability. It takes the control of PostgreSQL, overrides its -configuration and manages its lifecycle (start, stop, restart). Patroni is the only option for PostgreSQL 12 clustering and for cascading replication for Geo deployments. +Patroni is an opinionated solution for PostgreSQL high-availability. It takes the control of PostgreSQL, overrides its configuration, and manages its lifecycle (start, stop, restart). Patroni is the only option for PostgreSQL 12 clustering and for cascading replication for Geo deployments. -The [architecture](#example-recommended-setup-manual-steps) (that was mentioned above) does not change for Patroni. -You do not need any special consideration for Patroni while provisioning your database nodes. Patroni heavily relies on -Consul to store the state of the cluster and elect a leader. Any failure in Consul cluster and its leader election will -propagate to Patroni cluster as well. +The fundamental [architecture](#example-recommended-setup-manual-steps) (mentioned above) does not change for Patroni. +You do not need any special consideration for Patroni while provisioning your database nodes. Patroni heavily relies on Consul to store the state of the cluster and elect a leader. Any failure in Consul cluster and its leader election propagates to the Patroni cluster as well. -Patroni monitors the cluster and handles failover. When the primary node fails it works with Consul to notify PgBouncer. On failure, Patroni handles the transitioning of the old primary to a replica and rejoins it to the cluster automatically. +Patroni monitors the cluster and handles any failover. When the primary node fails it works with Consul to notify PgBouncer. On failure, Patroni handles the transitioning of the old primary to a replica and rejoins it to the cluster automatically. -With Patroni the connection flow is slightly different. Patroni on each node connects to Consul agent to join the -cluster. Only after this point it decides if the node is the primary or a replica. Based on this decision, it configures -and starts PostgreSQL which it communicates with directly over a Unix socket. This implies that if Consul cluster is not -functional or does not have a leader, Patroni and by extension PostgreSQL will not start. Patroni also exposes a REST -API which can be accessed via its [default port](https://docs.gitlab.com/omnibus/package-information/defaults.html#patroni) +With Patroni, the connection flow is slightly different. Patroni on each node connects to Consul agent to join the cluster. Only after this point it decides if the node is the primary or a replica. Based on this decision, it configures and starts PostgreSQL which it communicates with directly over a Unix socket. This means that if the Consul cluster is not functional or does not have a leader, Patroni and by extension PostgreSQL does not start. Patroni also exposes a REST API which can be accessed via its [default port](https://docs.gitlab.com/omnibus/package-information/defaults.html#patroni) on each node. ### Check replication status @@ -820,7 +812,7 @@ To verify the status of replication: echo 'select * from pg_stat_wal_receiver\x\g\x \n select * from pg_stat_replication\x\g\x' | gitlab-psql ``` -The same command can be run on all three database servers, and will return any information +The same command can be run on all three database servers. It returns any information about replication available depending on the role the server is performing. The leader should return one record per replica: @@ -920,7 +912,7 @@ patroni['remove_data_directory_on_rewind_failure'] = false patroni['remove_data_directory_on_diverged_timelines'] = false ``` -[The upstream documentation will always be more up to date](https://patroni.readthedocs.io/en/latest/SETTINGS.html#postgresql), but the table below should provide a minimal overview of functionality. +[The upstream documentation is always more up to date](https://patroni.readthedocs.io/en/latest/SETTINGS.html#postgresql), but the table below should provide a minimal overview of functionality. |Setting|Overview| |-|-| @@ -930,11 +922,11 @@ patroni['remove_data_directory_on_diverged_timelines'] = false ### Database authorization for Patroni -Patroni uses Unix socket to manage PostgreSQL instance. Therefore, the connection from the `local` socket must be trusted. +Patroni uses a Unix socket to manage the PostgreSQL instance. Therefore, a connection from the `local` socket must be trusted. Also, replicas use the replication user (`gitlab_replicator` by default) to communicate with the leader. For this user, you can choose between `trust` and `md5` authentication. If you set `postgresql['sql_replication_password']`, -Patroni will use `md5` authentication, otherwise it falls back to `trust`. You must to specify the cluster CIDR in +Patroni uses `md5` authentication, and otherwise falls back to `trust`. You must to specify the cluster CIDR in `postgresql['md5_auth_cidr_addresses']` or `postgresql['trust_auth_cidr_addresses']` respectively. ### Interacting with Patroni cluster @@ -943,18 +935,14 @@ You can use `gitlab-ctl patroni members` to check the status of the cluster memb `gitlab-ctl patroni` provides two additional sub-commands, `check-leader` and `check-replica` which indicate if a node is the primary or a replica. -When Patroni is enabled, you don't have direct control over `postgresql` service. Patroni will signal PostgreSQL's startup, -shutdown, and restart. For example, for shutting down PostgreSQL on a node, you must shutdown Patroni on the same node -with: +When Patroni is enabled, it exclusively controls PostgreSQL's startup, +shutdown, and restart. This means, to shut down PostgreSQL on a certain node you must shutdown Patroni on the same node with: ```shell sudo gitlab-ctl stop patroni ``` -Stopping or restarting Patroni service on the leader node will trigger the automatic failover. If you -want to signal Patroni to reload its configuration or restart PostgreSQL process without triggering the failover, you -must use the `reload` or `restart` sub-commands of `gitlab-ctl patroni` instead. These two sub-commands are wrappers of -the same `patronictl` commands. +Stopping or restarting the Patroni service on the leader node triggers an automatic failover. If you need Patroni to reload its configuration or restart the PostgreSQL process without triggering the failover, you must use the `reload` or `restart` sub-commands of `gitlab-ctl patroni` instead. These two sub-commands are wrappers of the same `patronictl` commands. ### Manual failover procedure for Patroni @@ -980,17 +968,17 @@ For further details on this subject, see the #### Geo secondary site considerations -When a Geo secondary site is replicating from a primary site that uses `Patroni` and `PgBouncer`, [replicating through PgBouncer is not supported](https://github.com/pgbouncer/pgbouncer/issues/382#issuecomment-517911529) and the secondary must replicate directly from the leader node in the `Patroni` cluster. Therefore, when there is an automatic or manual failover in the `Patroni` cluster, you will need to manually re-point your secondary site to replicate from the new leader with: +When a Geo secondary site is replicating from a primary site that uses `Patroni` and `PgBouncer`, [replicating through PgBouncer is not supported](https://github.com/pgbouncer/pgbouncer/issues/382#issuecomment-517911529). The secondary *must* replicate directly from the leader node in the `Patroni` cluster. When there is an automatic or manual failover in the `Patroni` cluster, you can manually re-point your secondary site to replicate from the new leader with: ```shell sudo gitlab-ctl replicate-geo-database --host=<new_leader_ip> --replication-slot=<slot_name> ``` -Otherwise, the replication will not happen anymore, even if the original node gets re-added as a follower node. This will re-sync your secondary site database and may take a long time depending on the amount of data to sync. You may also need to run `gitlab-ctl reconfigure` if replication is still not working after re-syncing. +Otherwise, the replication will not happen, even if the original node gets re-added as a follower node. This re-syncs your secondary site database and may take a long time depending on the amount of data to sync. You may also need to run `gitlab-ctl reconfigure` if replication is still not working after re-syncing. ### Recovering the Patroni cluster -To recover the old primary and rejoin it to the cluster as a replica, you can simply start Patroni with: +To recover the old primary and rejoin it to the cluster as a replica, you can start Patroni with: ```shell sudo gitlab-ctl start patroni @@ -1000,14 +988,13 @@ No further configuration or intervention is needed. ### Maintenance procedure for Patroni -With Patroni enabled, you can run a planned maintenance. If you want to do some maintenance work on one node and you -don't want Patroni to manage it, you can use put it into maintenance mode: +With Patroni enabled, you can run planned maintenance on your nodes. To perform maintenance on one node without Patroni, you can put it into maintenance mode with: ```shell sudo gitlab-ctl patroni pause ``` -When Patroni runs in a paused mode, it does not change the state of PostgreSQL. Once you are done you can resume Patroni: +When Patroni runs in a paused mode, it does not change the state of PostgreSQL. After you are done, you can resume Patroni: ```shell sudo gitlab-ctl patroni resume @@ -1018,9 +1005,7 @@ For further details, see [Patroni documentation on this subject](https://patroni ### Switching from repmgr to Patroni WARNING: -Although switching from repmgr to Patroni is fairly straightforward the other way around is not. Rolling back from -Patroni to repmgr can be complicated and may involve deletion of data directory. If you need to do that, please contact -GitLab support. +Switching from repmgr to Patroni is straightforward, the other way around is *not*. Rolling back from Patroni to repmgr can be complicated and may involve deletion of data directory. If you need to do that, please contact GitLab support. You can switch an exiting database cluster to use Patroni instead of repmgr with the following steps: @@ -1051,7 +1036,7 @@ You can switch an exiting database cluster to use Patroni instead of repmgr with 1. Repeat the last two steps for all replica nodes. `gitlab.rb` should look the same on all nodes. 1. If present, remove the `gitlab_repmgr` database and role on the primary. If you don't delete the `gitlab_repmgr` - database, upgrading PostgreSQL 11 to 12 will fail with: + database, upgrading PostgreSQL 11 to 12 fails with: ```plaintext could not load library "$libdir/repmgr_funcs": ERROR: could not access file "$libdir/repmgr_funcs": No such file or directory @@ -1059,9 +1044,7 @@ You can switch an exiting database cluster to use Patroni instead of repmgr with ### Upgrading PostgreSQL major version in a Patroni cluster -As of GitLab 13.3, PostgreSQL 11.7 and 12.3 are both shipped with Omnibus GitLab, and as of GitLab 13.7 -PostgreSQL 12 is used by default. If you want to upgrade to PostgreSQL 12 in versions prior to GitLab 13.7, -you must ask for it explicitly. +As of GitLab 13.3, PostgreSQL 11.7 and 12.3 are both shipped with Omnibus GitLab by default. As of GitLab 13.7, PostgreSQL 12 is the default. If you want to upgrade to PostgreSQL 12 in versions prior to GitLab 13.7, you must ask for it explicitly. WARNING: The procedure for upgrading PostgreSQL in a Patroni cluster is different than when upgrading using repmgr. @@ -1070,20 +1053,16 @@ upgrading PostgreSQL. Here are a few key facts that you must consider before upgrading PostgreSQL: -- The main point is that you will have to **shut down the Patroni cluster**. This means that your +- The main point is that you have to **shut down the Patroni cluster**. This means that your GitLab deployment is down for the duration of database upgrade or, at least, as long as your leader node is upgraded. This can be **a significant downtime depending on the size of your database**. -- Upgrading PostgreSQL creates a new data directory with a new control data. From Patroni's perspective - this is a new cluster that needs to be bootstrapped again. Therefore, as part of the upgrade procedure, - the cluster state (stored in Consul) is wiped out. Once the upgrade is completed, Patroni - bootstraps a new cluster. **This changes your _cluster ID_**. +- Upgrading PostgreSQL creates a new data directory with a new control data. From Patroni's perspective this is a new cluster that needs to be bootstrapped again. Therefore, as part of the upgrade procedure, the cluster state (stored in Consul) is wiped out. After the upgrade is complete, Patroni bootstraps a new cluster. **This changes your _cluster ID_**. -- The procedures for upgrading leader and replicas are not the same. That is why it is important to use the - right procedure on each node. +- The procedures for upgrading leader and replicas are not the same. That is why it is important to use the right procedure on each node. - Upgrading a replica node **deletes the data directory and resynchronizes it** from the leader using the - configured replication method (currently `pg_basebackup` is the only available option). It might take some + configured replication method (`pg_basebackup` is the only available option). It might take some time for replica to catch up with the leader, depending on the size of your database. - An overview of the upgrade procedure is outlined in [Patoni's documentation](https://patroni.readthedocs.io/en/latest/existing_data.html#major-upgrade-of-postgresql-version). @@ -1098,9 +1077,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade: ``` NOTE: - `gitlab-ctl pg-upgrade` tries to detect the role of the node. If for any reason the auto-detection - does not work or you believe it did not detect the role correctly, you can use the `--leader` or `--replica` - arguments to manually override it. + `gitlab-ctl pg-upgrade` tries to detect the role of the node. If for any reason the auto-detection does not work or you believe it did not detect the role correctly, you can use the `--leader` or `--replica` arguments to manually override it. 1. Stop Patroni **only on replicas**. @@ -1143,7 +1120,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade: ``` NOTE: -Reverting PostgreSQL upgrade with `gitlab-ctl revert-pg-upgrade` has the same considerations as +Reverting the PostgreSQL upgrade with `gitlab-ctl revert-pg-upgrade` has the same considerations as `gitlab-ctl pg-upgrade`. You should follow the same procedure by first stopping the replicas, then reverting the leader, and finally reverting the replicas. @@ -1151,11 +1128,11 @@ then reverting the leader, and finally reverting the replicas. ### Consul and PostgreSQL changes not taking effect -Due to the potential impacts, `gitlab-ctl reconfigure` only reloads Consul and PostgreSQL, it will not restart the services. However, not all changes can be activated by reloading. +Due to the potential impacts, `gitlab-ctl reconfigure` only reloads Consul and PostgreSQL, it does not restart the services. However, not all changes can be activated by reloading. To restart either service, run `gitlab-ctl restart SERVICE` -For PostgreSQL, it is usually safe to restart the master node by default. Automatic failover defaults to a 1 minute timeout. Provided the database returns before then, nothing else needs to be done. +For PostgreSQL, it is usually safe to restart the leader node by default. Automatic failover defaults to a 1 minute timeout. Provided the database returns before then, nothing else needs to be done. On the Consul server nodes, it is important to [restart the Consul service](../consul.md#restart-consul) in a controlled manner. @@ -1171,7 +1148,7 @@ PG::ConnectionBad: ERROR: pgbouncer cannot connect to server The problem may be that your PgBouncer node's IP address is not included in the `trust_auth_cidr_addresses` setting in `/etc/gitlab/gitlab.rb` on the database nodes. -You can confirm that this is the issue by checking the PostgreSQL log on the master +You can confirm that this is the issue by checking the PostgreSQL log on the leader database node. If you see the following error then `trust_auth_cidr_addresses` is the problem. @@ -1234,16 +1211,16 @@ To reset the Patroni state in Consul: /opt/gitlab/embedded/bin/consul kv delete -recurse /service/postgresql-ha/ ``` -1. Start one Patroni node, which will initialize the Patroni cluster and be elected as a leader. +1. Start one Patroni node, which initializes the Patroni cluster to elect as a leader. It's highly recommended to start the previous leader (noted in the first step), - in order to not lose existing writes that may have not been replicated because + so as to not lose existing writes that may have not been replicated because of the broken cluster state: ```shell sudo gitlab-ctl start patroni ``` -1. Start all other Patroni nodes that will join the Patroni cluster as replicas: +1. Start all other Patroni nodes that join the Patroni cluster as replicas: ```shell sudo gitlab-ctl start patroni @@ -1300,9 +1277,7 @@ Traceback (most recent call last): If the stack trace ends with `CFUNCTYPE(c_int)(lambda: None)`, this code triggers `MemoryError` if the Linux server has been hardened for security. -The code causes Python to write temporary executable files, and if it cannot find a filesystem -in which to do this, for example if `noexec` is set on the `/tmp` filesystem, it fails with -`MemoryError` ([read more in the issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6184)). +The code causes Python to write temporary executable files, and if it cannot find a file system in which to do this. For example, if `noexec` is set on the `/tmp` file system, it fails with `MemoryError` ([read more in the issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6184)). Workarounds: @@ -1310,7 +1285,7 @@ Workarounds: - If set to enforcing, SELinux may also prevent these operations. Verify the issue is fixed by setting SELinux to permissive. -Omnibus GitLab has shipped with Patroni since 13.1 along with a build of Python 3.7. +Patroni has been shipping with Omnibus GitLab since 13.1, along with a build of Python 3.7. Workarounds should stop being required when GitLab 14.x starts shipping with [a later version of Python](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6164) as the code which causes this was removed from Python 3.8. diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md index e11627b0c8f..0af4dbc8a7f 100644 --- a/doc/administration/reference_architectures/2k_users.md +++ b/doc/administration/reference_architectures/2k_users.md @@ -967,6 +967,143 @@ Read: - The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice). - About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss). +## Cloud Native Hybrid reference architecture with Helm Charts (alternative) + +As an alternative approach, you can also run select components of GitLab as Cloud Native +in Kubernetes via our official [Helm Charts](https://docs.gitlab.com/charts/). +In this setup, we support running the equivalent of GitLab Rails and Sidekiq nodes +in a Kubernetes cluster, named Webservice and Sidekiq respectively. In addition, +the following other supporting services are supported: NGINX, Task Runner, Migrations, +Prometheus, and Grafana. + +Hybrid installations leverage the benefits of both cloud native and traditional +compute deployments. With this, _stateless_ components can benefit from cloud native +workload management benefits while _stateful_ components are deployed in compute VMs +with Omnibus to benefit from increased permanence. + +The 2,000 reference architecture is not a highly-available setup. To achieve HA, you can follow a modified [3K reference architecture](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative). + +NOTE: +This is an **advanced** setup. Running services in Kubernetes is well known +to be complex. **This setup is only recommended** if you have strong working +knowledge and experience in Kubernetes. The rest of this +section assumes this. + +### Cluster topology + +The following tables and diagram detail the hybrid environment using the same formats +as the normal environment above. + +First are the components that run in Kubernetes. The recommendation at this time is to +use Google Cloud’s Kubernetes Engine (GKE) and associated machine types, but the memory +and CPU requirements should translate to most other providers. We hope to update this in the +future with further specific cloud provider details. + +| Service | Nodes<sup>1</sup> | Configuration | GCP | Allocatable CPUs and Memory | +|-------------------------------------------------------|-------------------|------------------------|-----------------|-----------------------------| +| Webservice | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | 23.7 vCPU, 16.9 GB memory | +| Sidekiq | 2 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | 3.9 vCPU, 11.8 GB memory | +| Supporting services such as NGINX, Prometheus | 2 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | 1.9 vCPU, 5.5 GB memory | + +<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix --> +<!-- markdownlint-disable MD029 --> +1. Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**. + In production deployments there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices. +<!-- markdownlint-enable MD029 --> + +Next are the backend components that run on static compute VMs via Omnibus (or External PaaS +services where applicable): + +| Service | Nodes | Configuration | GCP | +|--------------------------------------------|-------|-------------------------|------------------| +| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | +| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | +| Gitaly | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | +| Object storage<sup>3</sup> | n/a | n/a | n/a | + +<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix --> +<!-- markdownlint-disable MD029 --> +1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. Google Cloud SQL and AWS RDS are known to work, however Azure Database for PostgreSQL is [not recommended](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61) due to performance issues. Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery. +2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work. +3. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work. +<!-- markdownlint-enable MD029 --> + +NOTE: +For all PaaS solutions that involve configuring instances, it is strongly recommended to implement a minimum of three nodes in three different availability zones to align with resilient cloud architecture practices. + +```plantuml +@startuml 2k + +card "Kubernetes via Helm Charts" as kubernetes { + card "**External Load Balancer**" as elb #6a9be7 + + together { + collections "**Webservice** x3" as gitlab #32CD32 + collections "**Sidekiq** x2" as sidekiq #ff8dd1 + } + + card "**Prometheus + Grafana**" as monitor #7FFFD4 + card "**Supporting Services**" as support +} + +card "**Gitaly**" as gitaly #FF8C00 +card "**PostgreSQL**" as postgres #4EA7FF +card "**Redis**" as redis #FF6347 +cloud "**Object Storage**" as object_storage #white + +elb -[#6a9be7]-> gitlab +elb -[#6a9be7]--> monitor + +gitlab -[#32CD32]--> gitaly +gitlab -[#32CD32]--> postgres +gitlab -[#32CD32]-> object_storage +gitlab -[#32CD32]--> redis + +sidekiq -[#ff8dd1]--> gitaly +sidekiq -[#ff8dd1]-> object_storage +sidekiq -[#ff8dd1]---> postgres +sidekiq -[#ff8dd1]---> redis + +monitor .[#7FFFD4]u-> gitlab +monitor .[#7FFFD4]-> gitaly +monitor .[#7FFFD4]-> postgres +monitor .[#7FFFD4,norank]--> redis +monitor .[#7FFFD4,norank]u--> elb + +@enduml +``` + +### Resource usage settings + +The following formulas help when calculating how many pods may be deployed within resource constraints. +The [2k reference architecture example values file](https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/examples/ref/2k.yaml) +documents how to apply the calculated configuration to the Helm Chart. + +#### Webservice + +Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_. +Each Webservice pod consumes roughly 2 vCPUs and 2.5 GB of memory using +the [recommended topology](#cluster-topology) because two worker processes +are created by default and each pod has other small processes running. + +For 2,000 users we recommend a total Puma worker count of around 12. +With the [provided recommendations](#cluster-topology) this allows the deployment of up to 6 +Webservice pods with 2 workers per pod and 2 pods per node. Expand available resources using +the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional +Webservice pod. + +For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources). + +#### Sidekiq + +Sidekiq pods should generally have 1 vCPU and 2 GB of memory. + +[The provided starting point](#cluster-topology) allows the deployment of up to +2 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory +ratio for each additional pod. + +For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources). + <div align="right"> <a type="button" class="btn btn-default" href="#setup-components"> Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i> diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md index 1d3c57e5509..f4ae01c7442 100644 --- a/doc/administration/reference_architectures/3k_users.md +++ b/doc/administration/reference_architectures/3k_users.md @@ -2150,7 +2150,7 @@ services where applicable): | PostgreSQL<sup>1</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | | PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | | Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | -| Gitaly | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | +| Gitaly | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | | Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | | Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | | Object storage<sup>4</sup> | n/a | n/a | n/a | diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md index 2965b1202db..74d8bf39d03 100644 --- a/doc/administration/reference_architectures/index.md +++ b/doc/administration/reference_architectures/index.md @@ -71,6 +71,7 @@ The following reference architectures are available: The following Cloud Native Hybrid reference architectures, where select recommended components can be run in Kubernetes, are available: +- [Up to 2,000 users](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) - [Up to 3,000 users](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) - [Up to 5,000 users](5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) - [Up to 10,000 users](10k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index ee6e308d443..c55efe78216 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -554,6 +554,14 @@ end ## Users +### Create new user + +```ruby +u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password') +u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user will recieve confirmation e-mail +u.save! +``` + ### Skip reconfirmation ```ruby diff --git a/doc/ci/environments/incremental_rollouts.md b/doc/ci/environments/incremental_rollouts.md index 45b39654b51..e473d52f957 100644 --- a/doc/ci/environments/incremental_rollouts.md +++ b/doc/ci/environments/incremental_rollouts.md @@ -22,9 +22,9 @@ Manual and Timed rollouts are included automatically in projects controlled by [Auto DevOps](../../topics/autodevops/index.md), but they are also configurable through GitLab CI/CD in the `.gitlab-ci.yml` configuration file. -Manually triggered rollouts can be implemented with your [Continuously Delivery](../introduction/index.md#continuous-delivery) +Manually triggered rollouts can be implemented with your [Continuous Delivery](../introduction/index.md#continuous-delivery) methodology, while timed rollouts do not require intervention and can be part of your -[Continuously Deployment](../introduction/index.md#continuous-deployment) strategy. +[Continuous Deployment](../introduction/index.md#continuous-deployment) strategy. You can also combine both of them in a way that the app is deployed automatically unless you eventually intervene manually if necessary. diff --git a/doc/development/caching.md b/doc/development/caching.md new file mode 100644 index 00000000000..20847832e37 --- /dev/null +++ b/doc/development/caching.md @@ -0,0 +1,348 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Caching guidelines + +This document describes the various caching strategies in use at GitLab, how to implement +them effectively, and various gotchas. This material was extracted from the excellent +[Caching Workshop](https://gitlab.com/gitlab-org/create-stage/-/issues/12820). + +## What is a cache? + +A faster store for data, which is: + +- Used in many areas of computing. + - Processors have caches, hard disks have caches, lots of things have caches! +- Often closer to where you want the data to finally end up. +- A simpler store for data. +- Temporary. + +## What is fast? + +The goal for every web page should be to return in under 100ms: + +- This is achievable, but you need caching on a modern application. +- Larger responses take longer to build, and caching becomes critical to maintaining a constant speed. +- Cache reads are typically sub-1ms. There is very little that this doesn't improve. +- It's no good only being fast on subsequent page loads, as the initial experience + is important too, so this isn't a complete solution. +- User-specific data makes this challenging, and presents the biggest challenge + in refactoring existing applications to meet this speed goal. +- User-specific caches can still be effective but they just result in fewer cache + hits than generic caches shared between users. +- We're aiming to always have a majority of a page load pulled from the cache. + +## Why use a cache? + +- To make things faster! +- To avoid IO. + - Disk reads. + - Database queries. + - Network requests. +- To avoid recalculation of the same result multiple times: + - View rendering. + - JSON rendering. + - Markdown rendering. +- To provide redundancy. In some cases, caching can help disguise failures elsewhere, + such as CloudFlare's "Always Online" feature +- To reduce memory consumption. Processing less in Ruby but just fetching big strings +- To save money. Especially true in cloud computing, where processors are expensive compared to RAM. + +## Doubts about caching + +- Some engineers are opposed to caching except as a last resort, considering it to + be a hack, and that the real solution is to improve the underlying code to be faster. +- This is could be fed by fear of cache expiry, which is understandable. +- But caching is _still faster_. +- You must use both techniques to achieve true performance: + - There's no point caching if the initial cold write is so slow it times out, for example. + - But there are few cases where caching isn't a performance boost. +- However, you can totally use caching as a quick hack, and that's cool too. + Sometimes the "real" fix takes months, and caching takes only a day to implement. + +### Caching at GitLab + +Despite downsides to Redis caching, you should still feel free to make good use of the +caching setup inside the GitLab application and on GitLab.com. Our +[forecasting for cache utilization](https://gitlab-com.gitlab.io/gl-infra/tamland/saturation.html) +indicates we have plenty of headroom. + +## Workflow + +## Methodology + +1. Cache as close to your final user as possible. as often as possible. + - Caching your view rendering is by far the best performance improvement. +1. Try to cache as much data for as many users as possible: + - Generic data can be cached for everyone. + - You must keep this in mind when building new features. +1. Try to preserve cache data as much as possible: + - Use nested caches to maintain as much cached data as possible across expiries. +1. Perform as few requests to the cache as possible: + - This reduces variable latency caused by network issues. + - Lower overhead for each read on the cache. + +### Identify what benefits from caching + +Is the cache being added "worthy"? This can be hard to measure, but you can consider: + +- How large is the cached data? + - This might affect what type of cache storage you should use, such as storing + large HTML responses on disk rather than in RAM. +- How much I/O, CPU, and response time is saved by caching the data? + - If your cached data is large but the time taken to render it is low, such as + dumping a big chunk of text into the page, this might indicate the best place to cache it. +- How often is this data accessed? + - Caching frequently-accessed data usually has a greater effect. +- How often does this data change? + - If the cache rotates before the cache is read again, is this cache actually useful? + +### Tools + +#### Investigation + +- The performance bar is your first step when investigating locally and in production. + Look for expensive queries, excessive Redis calls, etc. +- Generate a flamegraph: add `?performance_bar=flamegraph` to the URL to help find + the methods where time is being spent. +- Dive into the Rails logs: + - Look closely at render times of partials too. + - To measure the response time alone, you can parse the JSON logs using `jq`: + - `tail -f log/development_json.log | jq ".duration_s"` + - `tail -f log/api_json.log | jq ".duration_s"` + - Some pointers for items to watch when you tail `development.log`: + - `tail -f log/development.log | grep "cache hits"` + - `tail -f log/development.log | grep "Rendered "` +- After you're looking in the right place: + - Remove or comment out sections of code until you find the cause. + - Use `binding.pry` to poke about in live requests. This requires a foreground + web process like [Thin](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/pry.md). + +#### Verification + +- Grafana, in particular the following dashboards: + - [`api: Rails Controller`](https://dashboards.gitlab.net/d/api-rails-controller/api-rails-controller?orgId=1) + - [`web: Rails Controller`](https://dashboards.gitlab.net/d/web-rails-controller/web-rails-controller?orgId=1) + - [`redis-cache: Overview`](https://dashboards.gitlab.net/d/redis-cache-main/redis-cache-overview?orgId=1) +- Logs + - For situations where Grafana charts don't cover what you need, use Kibana instead. +- Feature flags: + - It's nearly always worth using a feature flag when adding a cache. + - Toggle it on and off and watch the wiggly lines in Grafana. + - Expect response times to go up initially as the caches warm. + - The effect isn't obvious until you're running the flag at 100%. +- Performance bar: + - Use this locally and look for the cache calls in the Redis list. + - Also use this in production to verify your cache keys are what you expect. +- Flamegraphs: + - Append `?performance_bar=flamegraph` to the page + +## Cache levels + +### High level + +- HTTP caching: + - Use ETags and expiry times to instruct browsers to serve their own cached versions. + - This _does_ still hit Rails, but skips the view layer. +- HTTP caching in a reverse proxy cache: + - Same as above, but with a `public` setting. + - Instead of the browser, this instructs a reverse proxy (such as NGINX, HAProxy, Varnish) to serve a cached version. + - Subsequent requests never hit Rails. +- HTML page caching: + - Write a HTML file to disk + - Web server (such as NGINX, Apache, Caddy) serves the HTML file itself, skipping Rails. +- View or action caching + - Rails writes the entire rendered view into its cache store and serves it back. +- Fragment caching: + - Cache parts of a view in the Rails cache store. + - Cached parts are inserted into the view as it renders. + +### Low level + +1. Method caching: + - Calling the same method multiple times but only calculating the value once. + - Stored in Ruby memory. + - `@article ||= Article.find(params[:id])` + - `strong_memoize { Article.find(params[:id]) }` +1. Request caching: + - Return the same value for a key for the duration of a web request. + - `Gitlab::SafeRequestStore.fetch` +1. Read-through or write-through SQL caching: + - Cache sitting in front of the database. + - Rails does this within a request for the same query. +1. Novelty caches. +1. Hyper-specific caches for one use case. + +### Rails' built-in caching helpers + +This is well-documentation in the [Rails guides](https://guides.rubyonrails.org/caching_with_rails.html) + +- HTML page caching and action caching are no longer included by default, but they are still useful. +- The Rails guides call HTTP caching + [Conditional GET](https://guides.rubyonrails.org/caching_with_rails.html#conditional-get-support). +- For Rails' cache store, remember two very important (and almost identical) methods: + - `cache` in views, which is almost an alias for: + - `Rails.cache.fetch`, which you can use everywhere. +- `cache` includes a "template tree digest" which changes when you modify your view files. + +#### Rails cache options + +##### `expires_in` + +This sets the Time To Live (TTL) for the cache entry, and is the single most useful +(and most commonly used) cache option. This is supported in most Rails caching helpers. + +##### `race_condition_ttl` + +This option prevents multiple uncached hits for a key at the same time. +The first process that finds the key expired bumps the TTL by this amount, and it +then sets the new cache value. + +Used when a cache key is under very heavy load to prevent multiple simultaneous +writes, but should be set to a low value, such as 10 seconds. + +### When to use HTTP caching + +Use conditional GET caching when the entire response is cacheable: + +- No privacy risk when you aren't using public caches. You're only caching what + the user sees, for that user, in their browser. +- Particularly useful on [endpoints that get polled](polling.md#polling-with-etag-caching). +- Good examples: + - A list of discussions that we poll for updates. Use the last created entry's `updated_at` value for the `etag`. + - API endpoints. + +#### Possible downsides + +- Users and API libraries can ignore the cache. +- Sometimes Chrome does weird things with caches. +- You will forget it exists in development mode and get angry when your changes aren't appearing. +- In theory using conditional GET caching makes sense everywhere, but in practice it can + sometimes cause odd issues. + +### When to use view or action caching + +This is no longer very commonly used in the Rails world: + +- Support for it was removed from the Rails core. +- Usually better to look at reverse proxy caching or conditional GET responses. +- However it offers a somewhat simple way of emulating HTML page caching without + writing to disk, which makes it useful in cloud environments. +- Stores rather large chunks of markup in the cache store. +- We do have a custom implementation of this available on the API, where it is more + useful, in `cache_action`. + +### When to use fragment caching + +All the time! + +- Probably the most useful caching type to use in Rails, as it allows you to cache sections + of views, entire partials, collections of partials. +- Rendered collections of partials should be engineered with the goal of using + `cached: true` on them. +- It's faster to cache around the render call for a partial than inside the partial, + but then you lose out on the template tree digest, which means the caches don't expire + automatically when you update that partial. +- Beware of introducing lots of cache calls, such as placing a cache call inside a loop. + Sometimes it's unavoidable, but there are options for getting around this, like the partial collection caching. +- View rendering, and JSON generation, are slow, and should be cached wherever possible. + +### When to use method caching + +- Using instance variables, or [strong_memoize](utilities.md#strongmemoize) is something we all tend to do anyway. +- Useful when the same value is needed multiple times in a request. +- Can be used to prevent multiple cache calls for the same key. +- Can cause issues with ActiveRecord objects where a value doesn't change until you call + reload, which tends to crop up in the test suite. + +### When to use request caching + +- Similar usage pattern to method caching but can be used across multiple methods. +- Standardized way of storing something for the duration of a request. +- As the lookup is similar to a cache lookup (in the GitLab implementation), we can use + the same key for both. This is how `Gitlab::Cache.fetch_once` works. + +### When to use SQL caching + +Rails uses this automatically for identical queries in a request, so no action is +needed for that use case. + +- However, using a gem like `identity_cache` has a different purpose: caching queries + across multiple requests. +- Avoid using on single object lookups, like `Article.find(params[:id])`. +- Sometimes it's not possible to use the result, as it provides a read-only object. +- It can also cache relationships, useful in situations where we want to return a + list of things but don't care about filtering or ordering them differently. + +### When to use a novelty cache + +If you've exhausted other options, and must cache something that's really awkward, +it's time to look at a custom solution: + +- Examples in GitLab include `RepositorySetCache`, `RepositoryHashCache` and `AvatarCache`. +- Where possible, you should avoid creating custom cache implementations as it adds + inconsistency. +- Can be extremely effective. For example, the caching around `merged_branch_names`, + using [RepositoryHashCache](https://gitlab.com/gitlab-org/gitlab/-/issues/30536#note_290824711). + +## Cache expiration + +### How Redis expires keys + +In short: the oldest stuff is replaced with new stuff: + +- A [useful article](https://redis.io/topics/lru-cache) about configuring Redis as an LRU cache. +- Lots of options for different cache eviction strategies. +- You probably want `allkeys-lru`, which is functionally similar to Memcached. +- In Redis 4.0 and later, [allkeys-lfu is available](https://redis.io/topics/lru-cache#the-new-lfu-mode), + which is similar but different. +- We handle all explicit deletes using UNLINK instead of DEL now, which allows Redis to + reclaim memory in its own time, rather than immediately. + - This marks a key as deleted and returns a successful value quickly, + but actually deletes it later. + +### How Rails expires keys + +- Rails prefers using TTL and cache key expiry to using explicit deletes. +- Cache keys include a template tree digest by default when fragment caching in + views, which ensure any changes to the template automatically expire the cache. + - This isn't true in helpers, though, as a warning. +- Rails has two cache key methods on ActiveRecord objects: `cache_key_with_version` and `cache_key`. + The first one is used by default in version 5.2 and later, and is the standard behavior from before; + it includes the `updated_at` timestamp in the key. + +#### Cache key components + +Example found in the `application.log`: + +```plaintext +cache(@project, :tag_list) +views/projects/_home_panel:462ad2485d7d6957e03ceba2c6717c29/projects/16-2021031614242546945 +2/tag_list +``` + +1. The view name and template tree digest + `views/projects/_home_panel:462ad2485d7d6957e03ceba2c6717c29` +1. The model name, ID, and `updated_at` values + `projects/16-20210316142425469452` +1. The symbol we passed in, converted to a string + `tag_list` + +### Look for + +- User-specific data + - This is the most important! + - This isn't always obvious, particularly in views. + - You must trawl every helper method that's used in the area you want to cache. +- Time-specific data, such as "Billy posted this 8 minutes ago". +- Records being updated but not triggering the `updated_at` field to change +- Rails helpers roll the template digest into the keys in views, but this doesn't happen elsewhere, such as in helpers. +- `Grape::Entity` makes effective caching extremely difficult in the API layer. More on this later. +- Don't use `break` or `return` inside the fragment cache helper in views - it never writes a cache entry. +- Reordering items in a cache key that could return old data: + - such as having two values that could return `nil` and swapping them around. + - Use hashes, like `{ project: nil }` instead. +- Rails calls `#cache_key` on members of an array to find the keys, but it doesn't call it on values of hashes. diff --git a/doc/development/index.md b/doc/development/index.md index 6fd128e2a6f..e8e7369f6c5 100644 --- a/doc/development/index.md +++ b/doc/development/index.md @@ -257,6 +257,7 @@ the [reviewer values](https://about.gitlab.com/handbook/engineering/workflow/rev environments. - [Performance guidelines](performance.md) for writing code, benchmarks, and certain patterns to avoid. +- [Caching guidelines](caching.md) for using caching in Rails under a GitLab environment. - [Merge request performance guidelines](merge_request_performance_guidelines.md) for ensuring merge requests do not negatively impact GitLab performance - [Profiling](profiling.md) a URL, measuring performance using Sherlock, or diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md index 3e977749207..8ccdf8d0077 100644 --- a/doc/update/upgrading_postgresql_using_slony.md +++ b/doc/update/upgrading_postgresql_using_slony.md @@ -9,23 +9,23 @@ info: To determine the technical writer assigned to the Stage/Group associated w This guide describes the steps one can take to upgrade their PostgreSQL database to the latest version without the need for hours of downtime. This guide assumes you have two database servers: one database server running an older version of -PostgreSQL (e.g. 9.2.18) and one server running a newer version (e.g. 9.6.0). +PostgreSQL (for example, 9.2.18) and one server running a newer version (for example, 9.6.0). -For this process we use a PostgreSQL replication tool called -["Slony"](https://www.slony.info/). Slony allows replication between different +For this process, a PostgreSQL replication tool called +[Slony](https://www.slony.info/) is used. Slony allows replication between different PostgreSQL versions and as such can be used to upgrade a cluster with a minimal amount of downtime. -In various places we refer to the user `gitlab-psql`. This user should be the -user used to run the various PostgreSQL OS processes. If you're using a -different user (e.g. `postgres`) you should replace `gitlab-psql` with the name +This guide often refers to the user `gitlab-psql`, which is the +user used to run the various PostgreSQL OS processes. If you are using a +different user (for example, `postgres`), replace `gitlab-psql` with the name of said user. This guide also assumes your database is called `gitlabhq_production`. If you happen to use a different database name you should change this accordingly. ## Database Dumps -Slony only replicates data and not any schema changes. As a result we must +Slony only replicates data and not any schema changes. As a result you must ensure that all databases have the same database structure. To do so, generate a dump of the current database. This dump only @@ -40,10 +40,9 @@ If you're not using the Omnibus GitLab package you may have to adjust the paths `pg_dump` and the PostgreSQL installation directory to match the paths of your configuration. -After the structure dump is generated we also need to generate a dump for the +After the structure dump is generated, generate another dump for the `schema_migrations` table. This table doesn't have any primary keys and as such -can't be replicated easily by Slony. To generate this dump run the following -command on your active database server: +can't be replicated by Slony. To generate a dump of the `schema_migrations` table, run the following command on your active database server: ```shell sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql/ -p 5432 -U gitlab-psql -a -t schema_migrations -f /tmp/migrations.sql gitlabhq_production @@ -105,7 +104,7 @@ slonik version 2.2.5 ## Slony User -Next we must set up a PostgreSQL user that Slony can use to replicate your +Next, set up a PostgreSQL user that Slony can use to replicate your database. To do so, sign in to your production database using `psql` using a super-user account. After signing in, run the following SQL queries: @@ -123,8 +122,8 @@ later. ## Configuring Slony -We can now start configuring Slony. Slony uses a configuration file for -most of the work so we need to set this one up. This configuration file +You can now start configuring Slony. Slony uses a configuration file for +most of the work so it is important to set this up with care. Your configuration specifies where to put log files, how Slony should connect to the databases, etc. @@ -138,11 +137,11 @@ sudo chown gitlab-psql:root /var/log/gitlab/slony /var/run/slony1 /var/opt/gitla ``` Here `gitlab-psql` is the user used to run the PostgreSQL database processes. If -you're using a different user you should replace this with the name of said +you are using a different user you should replace this with the name of said user. -Now that the directories are in place we can create the configuration file. For -this we can use the following template: +Now that the directories are in place you can create the configuration file +by using the following template: ```perl if ($ENV{"SLONYNODES"}) { @@ -179,15 +178,14 @@ if ($ENV{"SLONYSET"}) { 1; ``` -In this configuration file you should replace a few placeholders before you can -use it. The following placeholders should be replaced: +Replace the following placeholders in this file to use it: - `OLD_HOST`: the address of the old database server. - `NEW_HOST`: the address of the new database server. - `SLONY_PASSWORD`: the password of the Slony user created earlier. - `TABLES`: the tables to replicate. -The list of tables to replicate can be generated by running the following +Generate the list of tables to replicate by running the following command on your old PostgreSQL database: ```shell @@ -212,12 +210,12 @@ this output, don't just append it below it. The result looks like this: After you have the configuration file generated you must install it on both the old and new database. To do so, place it in -`/var/opt/gitlab/postgresql/slony/slon_tools.conf` (for which we created the +`/var/opt/gitlab/postgresql/slony/slon_tools.conf` (for which you created the directory earlier on). -Now that the configuration file is in place we can _finally_ start replicating -our database. First we must set up the schema in our new database. To do so make -sure that the SQL files we generated earlier can be found in the `/tmp` +Now that the configuration file is in place, you can _finally_ start replicating +the database. First, set up the schema in the new database by making +sure that the SQL files generated earlier are in the `/tmp` directory of the new server. After these files are in place start a `psql` session on this server: @@ -250,8 +248,8 @@ following: ... more rows here ... ``` -Now we can initialize the required tables and what not that Slony uses for -its replication process. To do so, run the following on the old database: +Now you can initialize the required tables and other processes for +the replication process. To do so, run the following on the old database: ```shell sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_init_cluster --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik @@ -266,14 +264,14 @@ If all went well this produces something along the lines of: <stdin>:17: Please start a slon replication daemon for each node ``` -Next we need to start a replication node on every server. To do so, run the +Next, start a replication node on every server. To do so, run the following on the old database: ```shell sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf ``` -If all went well this produces output such as: +This should produce an output like the following: ```plaintext Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2 slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 & @@ -282,7 +280,7 @@ PID [26740] Start the watchdog process as well... ``` -Next we need to run the following command on the _new_ database server: +Next, run the following command on the _new_ database server: ```shell sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf @@ -290,14 +288,13 @@ sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab This produces similar output if all went well. -Next we need to tell the new database server what it should replicate. This can -be done by running the following command on the _new_ database server: +After Slony starts, you must tell the new database server what it should replicate. Run the following command on the _new_ database server: ```shell sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_create_set 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik ``` -This should produce output along the lines of the following: +This should produce an output like the following: ```plaintext <stdin>:11: Subscription set 1 (set1) created @@ -310,7 +307,7 @@ This should produce output along the lines of the following: <stdin>:328: All tables added ``` -Finally we can start the replication process by running the following on the +Finally, you can start the replication process by running the following on the _new_ database server: ```shell @@ -357,17 +354,14 @@ main ``` This script compares the sizes of the old and new database every minute and -print the result to STDOUT as well as logging it to a file. Make sure to replace +prints the results to STDOUT as well as logging it to a file. Make sure to replace `SLONY_PASSWORD`, `OLD_HOST`, and `NEW_HOST` with the correct values. ## Stopping Replication -At some point, the two databases are in sync. If this is the case, you must plan -for a few minutes of downtime. This small downtime window is used to stop the -replication process, remove any Slony data from both databases, and restart -GitLab so it can use the new database. +Eventually, the two databases become in sync. At this point, there is a few minutes of downtime that you must plan for before the replicated database is available. During this time, the replication process should stop and all Slony data should be removed from both databases. After the replication process finishes, GitLab can restart and is able to use the newly-replicated database. -First, let's stop all of GitLab. Omnibus users can do so by running the +First, stop all of GitLab. Omnibus users can do so by running the following on their GitLab servers: ```shell @@ -378,10 +372,10 @@ sudo gitlab-ctl stop mailroom If you have any other processes that use PostgreSQL, you should also stop those. -After everything has been stopped, be sure to update any configuration settings +After everything successfully stops, be sure to update any configuration settings and DNS records so they all point to the new database. -When the settings have been taken care of, we need to stop the replication +When the configuration is complete, stop the replication process. It's crucial that no new data is written to the databases at this point, as this data is discarded. @@ -473,14 +467,14 @@ This corrects the ownership of sequences and reset the next value for the ## Removing Slony -Next we need to remove all Slony related data. To do so, run the following +The final step is to remove all Slony related data. To do so, run the following command on the _target_ server: ```shell sudo gitlab-psql gitlabhq_production -c "DROP SCHEMA _slony_replication CASCADE;" ``` -Once done you can safely remove any Slony related files (e.g. the log +Once done you can safely remove any Slony related files (for example, the log directory), and uninstall Slony if desired. At this point you can start your GitLab instance again and if all went well it should be using your new database server. diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md index 165826a1054..7cb5db4379a 100644 --- a/doc/user/analytics/index.md +++ b/doc/user/analytics/index.md @@ -15,7 +15,8 @@ When we describe GitLab analytics, we use the following terms: - **DORA (DevOps Research and Assessment)** ["Four Keys"](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance): - **Speed/Velocity** - - **Deployment frequency:** The average number of successful deployments to production per period. + - **Deployment frequency:** The relative frequency of successful deployments to production + (hourly, daily, weekly, monthly, or yearly). This effectively measures how often you are delivering value to end users. A higher deployment frequency means you are able to get feedback and iterate more quickly in delivering improvements and features faster. GitLab measures this as the number of deployments to a diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb index d245cc68a52..2f78e4e5c0a 100644 --- a/lib/gitlab/experimentation.rb +++ b/lib/gitlab/experimentation.rb @@ -34,10 +34,6 @@ module Gitlab module Experimentation EXPERIMENTS = { - invite_members_empty_group_version_a: { - tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyGroupVersionA', - use_backwards_compatible_subject_index: true - }, remove_known_trial_form_fields_welcoming: { tracking_category: 'Growth::Conversion::Experiment::RemoveKnownTrialFormFieldsWelcoming', rollout_strategy: :user diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index ac47c5be1e8..96e3a015115 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -44,7 +44,7 @@ gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" gitlab_pages_enabled=false gitlab_pages_dir=$(cd $app_root/../gitlab-pages 2> /dev/null && pwd) gitlab_pages_pid_path="$pid_path/gitlab-pages.pid" -gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090" +gitlab_pages_options="-config $app_root/gitlab-pages/gitlab-pages.conf" gitlab_pages_log="$app_root/log/gitlab-pages.log" shell_path="/bin/bash" gitaly_enabled=true diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example index 53bebe55fa3..0233c26cecc 100644 --- a/lib/support/init.d/gitlab.default.example +++ b/lib/support/init.d/gitlab.default.example @@ -68,7 +68,7 @@ gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" # The -pages-domain must be specified the same as in `gitlab.yml > pages > host`. # Set `gitlab_pages_enabled=true` if you want to enable the Pages feature. gitlab_pages_enabled=false -gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090" +gitlab_pages_options="-config $app_root/gitlab-pages/gitlab-pages.conf" gitlab_pages_log="$app_root/log/gitlab-pages.log" # mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4dfb8b65547..7f3ee036c07 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -18218,9 +18218,6 @@ msgstr "" msgid "Invite members" msgstr "" -msgid "Invite your team" -msgstr "" - msgid "InviteEmail|%{inviter} invited you to join the %{project_or_group_name} %{project_or_group} as a %{role}" msgstr "" diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index 8aba1356fd0..9debdc1d4dd 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -236,7 +236,7 @@ module QA if disabled.nil? has_css?(element_selector_css(name, kwargs), text: text, wait: wait, class: klass, visible: visible) else - find_element(name, original_kwargs).disabled? == disabled + find_element(name, **original_kwargs).disabled? == disabled end end diff --git a/spec/helpers/invite_members_helper_spec.rb b/spec/helpers/invite_members_helper_spec.rb index e08ce09702f..4f44acecbd9 100644 --- a/spec/helpers/invite_members_helper_spec.rb +++ b/spec/helpers/invite_members_helper_spec.rb @@ -126,42 +126,4 @@ RSpec.describe InviteMembersHelper do end end end - - context 'with group' do - let_it_be(:group) { create(:group) } - - describe "#invite_group_members?" do - context 'when the user is an owner' do - before do - group.add_owner(owner) - allow(helper).to receive(:current_user) { owner } - end - - it 'returns false' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_empty_group_version_a) { false } - - expect(helper.invite_group_members?(group)).to eq false - end - - it 'returns true' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_empty_group_version_a) { true } - - expect(helper.invite_group_members?(group)).to eq true - end - end - - context 'when the user is a developer' do - before do - group.add_developer(developer) - allow(helper).to receive(:current_user) { developer } - end - - it 'returns false' do - allow(helper).to receive(:experiment_enabled?).with(:invite_members_empty_group_version_a) { true } - - expect(helper.invite_group_members?(group)).to eq false - end - end - end - end end diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb index bd7aa28746c..c486538a260 100644 --- a/spec/lib/gitlab/experimentation_spec.rb +++ b/spec/lib/gitlab/experimentation_spec.rb @@ -2,19 +2,6 @@ require 'spec_helper' -# As each associated, backwards-compatible experiment gets cleaned up and removed from the EXPERIMENTS list, its key will also get removed from this list. Once the list here is empty, we can remove the backwards compatibility code altogether. -# Originally created as part of https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45733 for https://gitlab.com/gitlab-org/gitlab/-/issues/270858. -RSpec.describe Gitlab::Experimentation::EXPERIMENTS do - it 'temporarily ensures we know what experiments exist for backwards compatibility' do - expected_experiment_keys = [:invite_members_empty_group_version_a] - - backwards_compatible_experiment_keys = described_class.filter { |_, v| v[:use_backwards_compatible_subject_index] }.keys - - expect(backwards_compatible_experiment_keys).not_to be_empty, "Oh, hey! Let's clean up that :use_backwards_compatible_subject_index stuff now :D" - expect(backwards_compatible_experiment_keys).to match(expected_experiment_keys) - end -end - RSpec.describe Gitlab::Experimentation do using RSpec::Parameterized::TableSyntax |
