diff options
29 files changed, 272 insertions, 320 deletions
diff --git a/.markdownlint.yml b/.markdownlint.yml index 4e852880d26..dd04406f68d 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -130,7 +130,6 @@ "Ubuntu", "Ultra Auth", "Unicorn", - "unicorn-worker-killer", "URL", "WebdriverIO", "YAML", @@ -185,7 +185,6 @@ gem 'rack-timeout', '~> 0.5.1', require: 'rack/timeout/base' group :unicorn do gem 'unicorn', '~> 5.5' - gem 'unicorn-worker-killer', '~> 0.4.4' end group :puma do diff --git a/Gemfile.lock b/Gemfile.lock index c70f1710d50..62f5eb4d3a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1318,9 +1318,6 @@ GEM unicorn (5.5.5) kgio (~> 2.6) raindrops (~> 0.7) - unicorn-worker-killer (0.4.4) - get_process_mem (~> 0) - unicorn (>= 4, < 6) uniform_notifier (1.13.0) unleash (0.1.5) murmurhash3 (~> 0.1.6) @@ -1653,7 +1650,6 @@ DEPENDENCIES u2f (~> 0.2.1) unf (~> 0.1.4) unicorn (~> 5.5) - unicorn-worker-killer (~> 0.4.4) unleash (~> 0.1.5) valid_email (~> 0.1) validates_hostname (~> 1.0.11) diff --git a/app/assets/javascripts/blob/components/table_contents.vue b/app/assets/javascripts/blob/components/table_contents.vue index 3a0a385d494..78ecb82f2cd 100644 --- a/app/assets/javascripts/blob/components/table_contents.vue +++ b/app/assets/javascripts/blob/components/table_contents.vue @@ -60,7 +60,7 @@ export default { </script> <template> - <gl-dropdown v-if="!isHidden && items.length" icon="list-bulleted" class="gl-mr-2"> + <gl-dropdown v-if="!isHidden && items.length" icon="list-bulleted" class="gl-mr-2" lazy> <gl-dropdown-item v-for="(item, index) in items" :key="index" :href="`#${item.anchor}`"> <span :style="{ 'padding-left': `${item.spacing}px` }" diff --git a/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue b/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue index fe56833016e..8ddf50cb357 100644 --- a/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue +++ b/app/assets/javascripts/boards/components/issue_time_estimate_deprecated.vue @@ -10,7 +10,7 @@ export default { }, props: { estimate: { - type: Number, + type: [Number, String], required: true, }, }, diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue index ebe9c4eee2f..1bb847217d1 100644 --- a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue +++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue @@ -74,6 +74,11 @@ export default { keys: ['label', 'code', 'proxy', 'error'], }, { + metric: 'memory', + header: s__('PerformanceBar|Memory'), + keys: ['item_header', 'item_content'], + }, + { metric: 'total', header: s__('PerformanceBar|Frontend resources'), keys: ['name', 'size'], diff --git a/app/views/projects/blob/_viewer.html.haml b/app/views/projects/blob/_viewer.html.haml index a0d82ffd2c7..8713ce79d96 100644 --- a/app/views/projects/blob/_viewer.html.haml +++ b/app/views/projects/blob/_viewer.html.haml @@ -5,6 +5,7 @@ - external_embed = local_assigns.fetch(:external_embed, false) - viewer_url = local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: viewer.type, format: :json)) } if load_async +- add_page_startup_api_call viewer_url .blob-viewer{ data: { type: viewer.type, rich_type: rich_type, url: viewer_url, path: viewer.blob.path }, class: ('hidden' if hidden) } - if render_error = render 'projects/blob/render_error', viewer: viewer diff --git a/config.ru b/config.ru index 0c50b3fdf6f..ed76239ef2e 100644 --- a/config.ru +++ b/config.ru @@ -2,25 +2,10 @@ # This file is used by Rack-based servers to start the application. -if defined?(Unicorn) - require 'unicorn' - - if ENV['RAILS_ENV'] == 'production' || ENV['RAILS_ENV'] == 'staging' - # Unicorn self-process killer - require 'unicorn/worker_killer' - - min = (ENV['GITLAB_UNICORN_MEMORY_MIN'] || 400 * 1 << 20).to_i - max = (ENV['GITLAB_UNICORN_MEMORY_MAX'] || 650 * 1 << 20).to_i - - # Max memory size (RSS) per worker - use Unicorn::WorkerKiller::Oom, min, max - end -end - require ::File.expand_path('../config/environment', __FILE__) def master_process? - Prometheus::PidProvider.worker_id.in? %w(unicorn_master puma_master) + Prometheus::PidProvider.worker_id == 'puma_master' end warmup do |app| diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 774254d2ff1..d76dd8fe25b 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -131,13 +131,6 @@ :versions: [] :when: 2016-05-02 05:45:28.817510000 Z - - :license - - unicorn-worker-killer - - ruby - - :who: Connor Shea - :why: https://github.com/kzk/unicorn-worker-killer/blob/master/LICENSE - :versions: [] - :when: 2016-05-02 05:45:38.323867000 Z -- - :license - unf - BSD - :who: Connor Shea diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 85bfc4f0214..6fd92865731 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -15,9 +15,15 @@ Peek.into Peek::Views::Elasticsearch Peek.into Peek::Views::Rugged Peek.into Peek::Views::ExternalHttp Peek.into Peek::Views::BulletDetailed if defined?(Bullet) +Peek.into Peek::Views::Memory Peek.into Peek::Views::Tracing if Labkit::Tracing.tracing_url_enabled? +# Trigger view creation here, since views might be subscribing to Rails notifications +# via setup_subscribers, which is called in the initializer. +# See https://github.com/peek/peek/blob/master/lib/peek/views/view.rb +Peek.views + ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |_name, _start, _finish, _id, payload| if request_id = payload[:env]['action_dispatch.request_id'] Peek.adapter.save(request_id) diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md index a168584e754..f98cddb52c9 100644 --- a/doc/administration/environment_variables.md +++ b/doc/administration/environment_variables.md @@ -32,8 +32,6 @@ You can use the following environment variables to override certain values: | `GITLAB_HOST` | string | The full URL of the GitLab server (including `http://` or `https://`). | | `GITLAB_ROOT_PASSWORD` | string | Sets the password for the `root` user on installation. | | `GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN` | string | Sets the initial registration token used for runners. | -| `GITLAB_UNICORN_MEMORY_MAX` | integer | The maximum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). | -| `GITLAB_UNICORN_MEMORY_MIN` | integer | The minimum memory threshold (in bytes) for the [unicorn-worker-killer](operations/unicorn.md#unicorn-worker-killer). | | `RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging`, or `test`. | | `UNSTRUCTURED_RAILS_LOG` | string | Enables the unstructured log in addition to JSON logs (defaults to `true`). | diff --git a/doc/administration/logs.md b/doc/administration/logs.md index 8894f46aa26..5921406973b 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -611,41 +611,6 @@ This file lives in `/var/log/gitlab/puma/puma_stderr.log` for Omnibus GitLab packages, or in `/home/git/gitlab/log/puma_stderr.log` for installations from source. -## Unicorn Logs - -Starting with GitLab 13.0, Puma is the default web server used in GitLab -all-in-one package based installations, and GitLab Helm chart deployments. - -### `unicorn_stdout.log` - -This file lives in `/var/log/gitlab/unicorn/unicorn_stdout.log` for -Omnibus GitLab packages or in `/home/git/gitlab/log/unicorn_stdout.log` for -for installations from source. - -### `unicorn_stderr.log` - -This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for -Omnibus GitLab packages or in `/home/git/gitlab/log/unicorn_stderr.log` for -for installations from source. - -These logs contain all information about the state of Unicorn processes at any given time. - -```plaintext -I, [2015-02-13T06:14:46.680381 #9047] INFO -- : Refreshing Gem list -I, [2015-02-13T06:14:56.931002 #9047] INFO -- : listening on addr=127.0.0.1:8080 fd=12 -I, [2015-02-13T06:14:56.931381 #9047] INFO -- : listening on addr=/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket fd=13 -I, [2015-02-13T06:14:56.936638 #9047] INFO -- : master process ready -I, [2015-02-13T06:14:56.946504 #9092] INFO -- : worker=0 spawned pid=9092 -I, [2015-02-13T06:14:56.946943 #9092] INFO -- : worker=0 ready -I, [2015-02-13T06:14:56.947892 #9094] INFO -- : worker=1 spawned pid=9094 -I, [2015-02-13T06:14:56.948181 #9094] INFO -- : worker=1 ready -W, [2015-02-13T07:16:01.312916 #9094] WARN -- : #<Unicorn::HttpServer:0x0000000208f618>: worker (pid: 9094) exceeds memory limit (320626688 bytes > 247066940 bytes) -W, [2015-02-13T07:16:01.313000 #9094] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 9094) alive: 3621 sec (trial 1) -I, [2015-02-13T07:16:01.530733 #9047] INFO -- : reaped #<Process::Status: pid 9094 exit 0> worker=1 -I, [2015-02-13T07:16:01.534501 #13379] INFO -- : worker=1 spawned pid=13379 -I, [2015-02-13T07:16:01.534848 #13379] INFO -- : worker=1 ready -``` - ## `repocheck.log` This file lives in `/var/log/gitlab/gitlab-rails/repocheck.log` for diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md index 72ab536ceae..780e5b47fba 100644 --- a/doc/administration/monitoring/performance/performance_bar.md +++ b/doc/administration/monitoring/performance/performance_bar.md @@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Performance Bar **(FREE SELF)** -> The **Stats** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271551) in GitLab SaaS 13.9. +> The **Stats** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271551) in GitLab 13.9. +> The **Memory** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/330736) in GitLab 14.0. You can display the GitLab Performance Bar to see statistics for the performance of a page. When activated, it looks as follows: @@ -40,9 +41,11 @@ From left to right, it displays: Time until something was visible to the user. - [**DomContentLoaded**](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp) Event. - **Total number of requests** the page loaded. -- **Trace**: If Jaeger is integrated, **Trace** links to a Jaeger tracing page +- **Memory**: the amount of memory consumed and objects allocated during the selected request. + Select it to display a window with more details. +- **Trace**: if Jaeger is integrated, **Trace** links to a Jaeger tracing page with the current request's `correlation_id` included. -- **+**: A link to add a request's details to the performance bar. The request +- **+**: a link to add a request's details to the performance bar. The request can be added by its full URL (authenticated as the current user), or by the value of its `X-Request-Id` header. - **Download**: a link to download the raw JSON used to generate the Performance Bar reports. @@ -52,6 +55,11 @@ From left to right, it displays: - **Stats** (optional): if the `GITLAB_PERFORMANCE_BAR_STATS_URL` environment variable is set, this URL is displayed in the bar. In GitLab 13.9 and later, used only in GitLab SaaS. +NOTE: +Not all indicators are available in all environments. For instance, the memory view +requires to run Ruby with [specific patches](https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/patches/ruby/2.7.2/thread-memory-allocations-2.7.patch) applied. +When running GitLab locally using the GDK this is typically not the case and the memory view cannot be used. + ## Request warnings Requests that exceed predefined limits display a warning **{warning}** icon and diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md index 708861d8529..268c9281d87 100644 --- a/doc/administration/operations/index.md +++ b/doc/administration/operations/index.md @@ -22,7 +22,6 @@ Keep your GitLab instance up and running smoothly. to restart Sidekiq. - [Multiple Sidekiq processes](extra_sidekiq_processes.md): Configure multiple Sidekiq processes to ensure certain queues always have dedicated workers, no matter the number of jobs that need to be processed. **(FREE SELF)** - [Puma](puma.md): Understand Puma and puma-worker-killer. -- [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer. - Speed up SSH operations by [Authorizing SSH users via a fast, indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or by [doing away with user SSH keys stored on GitLab entirely in favor diff --git a/doc/administration/operations/unicorn.md b/doc/administration/operations/unicorn.md deleted file mode 100644 index 03995ee05ba..00000000000 --- a/doc/administration/operations/unicorn.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -stage: Enablement -group: Distribution -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments ---- - -# Understanding Unicorn and unicorn-worker-killer - -NOTE: -Starting with GitLab 13.0, Puma is the default web server used in GitLab -all-in-one package based installations as well as GitLab Helm chart deployments. - -## Unicorn - -GitLab uses [Unicorn](https://yhbt.net/unicorn/), a pre-forking Ruby web -server, to handle web requests (web browsers and Git HTTP clients). Unicorn is -a daemon written in Ruby and C that can load and run a Ruby on Rails -application; in our case the Rails application is GitLab Community Edition or -GitLab Enterprise Edition. - -Unicorn has a multi-process architecture to make better use of available CPU -cores (processes can run on different cores) and to have stronger fault -tolerance (most failures stay isolated in only one process and cannot take down -GitLab entirely). On startup, the Unicorn 'master' process loads a clean Ruby -environment with the GitLab application code, and then spawns 'workers' which -inherit this clean initial environment. The 'master' never handles any -requests, that is left to the workers. The operating system network stack -queues incoming requests and distributes them among the workers. - -In a perfect world, the master would spawn its pool of workers once, and then -the workers handle incoming web requests one after another until the end of -time. In reality, worker processes can crash or time out: if the master notices -that a worker takes too long to handle a request it will terminate the worker -process with SIGKILL ('kill -9'). No matter how the worker process ended, the -master process will replace it with a new 'clean' process again. Unicorn is -designed to be able to replace 'crashed' workers without dropping user -requests. - -This is what a Unicorn worker timeout looks like in `unicorn_stderr.log`. The -master process has PID 56227 below. - -```plaintext -[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing -[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10 -[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538 -[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready -``` - -### Tunable options - -The main tunable options for Unicorn are the number of worker processes and the -request timeout after which the Unicorn master terminates a worker process. -See the [Omnibus GitLab Unicorn settings -documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html) -if you want to adjust these settings. - -## unicorn-worker-killer - -GitLab has memory leaks. These memory leaks manifest themselves in long-running -processes, such as Unicorn workers. (The Unicorn master process is not known to -leak memory, probably because it does not handle user requests.) - -To make these memory leaks manageable, GitLab comes with the -[unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This -gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn -workers to do a memory self-check after every 16 requests. If the memory of the -Unicorn worker exceeds a pre-set limit then the worker process exits. The -Unicorn master then automatically replaces the worker process. - -This is a robust way to handle memory leaks: Unicorn is designed to handle -workers that 'crash' so no user requests will be dropped. The -unicorn-worker-killer gem is designed to only terminate a worker process _in -between requests_, so no user requests are affected. You can set the minimum and -maximum memory threshold (in bytes) for the Unicorn worker killer by -setting the following values `/etc/gitlab/gitlab.rb`: - -- For GitLab **12.7** and newer: - - ```ruby - unicorn['worker_memory_limit_min'] = "1024 * 1 << 20" - unicorn['worker_memory_limit_max'] = "1280 * 1 << 20" - ``` - -- For GitLab **12.6** and older: - - ```ruby - unicorn['worker_memory_limit_min'] = "400 * 1 << 20" - unicorn['worker_memory_limit_max'] = "650 * 1 << 20" - ``` - -Otherwise, you can set the `GITLAB_UNICORN_MEMORY_MIN` and `GITLAB_UNICORN_MEMORY_MAX` -[environment variables](../environment_variables.md). - -This is what a Unicorn worker memory restart looks like in unicorn_stderr.log. -You see that worker 4 (PID 125918) is inspecting itself and decides to exit. -The threshold memory value was 254802235 bytes, about 250MB. With GitLab this -threshold is a random value between 200 and 250 MB. The master process (PID -117565) then reaps the worker process and spawns a new 'worker 4' with PID -127549. - -```plaintext -[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes) -[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1) -[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4 -[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549 -[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready -``` - -One other thing that stands out in the log snippet above, taken from -GitLab.com, is that 'worker 4' was serving requests for only 23 seconds. This -is a normal value for our current GitLab.com setup and traffic. - -The high frequency of Unicorn memory restarts on some GitLab sites can be a -source of confusion for administrators. Usually they are a [red -herring](https://en.wikipedia.org/wiki/Red_herring). diff --git a/doc/development/architecture.md b/doc/development/architecture.md index fdcaa91a639..d8d452240ff 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -710,23 +710,6 @@ disabled by default. [Puma](https://puma.io/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often this displays in process output as `bundle` or `config.ru` depending on the GitLab version. -#### Unicorn - -Starting with GitLab 13.0, Puma is the default web server and Unicorn has been -disabled by default. - -- [Project page](https://gitlab.com/gitlab-org/gitlab/blob/master/README.md) -- Configuration: - - [Omnibus](https://docs.gitlab.com/omnibus/settings/unicorn.html) - - [Charts](https://docs.gitlab.com/charts/charts/gitlab/webservice/) - - [Source](../install/installation.md#configure-it) - - [GDK](https://gitlab.com/gitlab-org/gitlab/blob/master/config/gitlab.yml.example) -- Layer: Core Service (Processor) -- Process: `unicorn` -- GitLab.com: [Unicorn](../user/gitlab_com/index.md#unicorn) - -[Unicorn](https://yhbt.net/unicorn/) is a Ruby application server that is used to run the core Rails Application that provides the user facing features in GitLab. Often this displays in process output as `bundle` or `config.ru` depending on the GitLab version. - #### LDAP Authentication - Configuration: diff --git a/doc/development/usage_ping/metrics_dictionary.md b/doc/development/usage_ping/metrics_dictionary.md index 9fd09714e65..c888a9858a6 100644 --- a/doc/development/usage_ping/metrics_dictionary.md +++ b/doc/development/usage_ping/metrics_dictionary.md @@ -57,6 +57,18 @@ Metric definitions can have one of the following statuses: - `deprecated`: Metric is deprecated and possibly planned to be removed. - `removed`: Metric was removed, but it may appear in Usage Ping payloads sent from instances running on older versions of GitLab. +### Metric value_type + +Metric definitions can have one of the following values for `value_type`: + +- `boolean` +- `number` +- `string` +- `object`: A metric with `value_type: object` must have `value_json_schema` with a link to the JSON schema for the object. +In general, we avoid complex objects and prefer one of the `boolean`, `number`, or `string` value types. +An example of a metric that uses `value_type: object` is `topology` (`/config/metrics/settings/20210323120839_topology.yml`), +which has a related schema in `/config/metrics/objects_schemas/topology_schema.json`. + ### Metric name To improve metric discoverability by a wider audience, each metric with diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index cdb4ca52c9c..f05e5f00bc2 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -508,20 +508,6 @@ of proposed changes can be found at GitLab.com uses the default of 60 seconds for [Puma request timeouts](https://docs.gitlab.com/omnibus/settings/puma.html#worker-timeout). -## Unicorn - -GitLab.com adjusts the memory limits for the [unicorn-worker-killer](https://rubygems.org/gems/unicorn-worker-killer) gem. - -Base default: - -- `memory_limit_min` = 750MiB -- `memory_limit_max` = 1024MiB - -Web front-ends: - -- `memory_limit_min` = 1024MiB -- `memory_limit_max` = 1280MiB - ## GitLab.com-specific rate limits NOTE: diff --git a/lib/peek/views/memory.rb b/lib/peek/views/memory.rb new file mode 100644 index 00000000000..399474dedf1 --- /dev/null +++ b/lib/peek/views/memory.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Peek + module Views + class Memory < View + MEM_TOTAL_LABEL = 'Total' + MEM_OBJECTS_LABEL = 'Objects allocated' + MEM_MALLOCS_LABEL = 'Allocator calls' + MEM_BYTES_LABEL = 'Large allocations' + + def initialize(options = {}) + super + + @thread_memory = {} + end + + def results + return thread_memory if thread_memory.empty? + + { + calls: byte_string(thread_memory[:mem_total_bytes]), + summary: { + MEM_OBJECTS_LABEL => number_string(thread_memory[:mem_objects]), + MEM_MALLOCS_LABEL => number_string(thread_memory[:mem_mallocs]), + MEM_BYTES_LABEL => byte_string(thread_memory[:mem_bytes]) + }, + details: [ + { + item_header: MEM_TOTAL_LABEL, + item_content: "Total memory use of this request. This includes both occupancy of existing heap slots " \ + "as well as newly allocated memory due to large objects. Not adjusted for freed memory. " \ + "Lower is better." + }, + { + item_header: MEM_OBJECTS_LABEL, + item_content: "Total number of objects allocated by the Ruby VM during this request. " \ + "Not adjusted for objects that were freed again. Lower is better." + }, + { + item_header: MEM_MALLOCS_LABEL, + item_content: "Total number of times Ruby had to call `malloc`, the C memory allocator. " \ + "This is necessary for objects that are too large to fit into a 40 Byte slot in Ruby's managed heap. " \ + "Lower is better." + }, + { + item_header: MEM_BYTES_LABEL, + item_content: "Memory allocated for objects that did not fit into a heap slot. " \ + "Not adjusted for memory that was freed again. Lower is better." + } + ] + } + end + + private + + attr_reader :thread_memory + + def setup_subscribers + subscribe 'process_action.action_controller' do + # Ensure that Peek will see memory instrumentation in `results` by triggering it when + # a request is done processing. Peek itself hooks into the same notification: + # https://github.com/peek/peek/blob/master/lib/peek/railtie.rb + Gitlab::InstrumentationHelper.instrument_thread_memory_allocations(thread_memory) + end + end + + def byte_string(bytes) + ActiveSupport::NumberHelper.number_to_human_size(bytes) + end + + def number_string(num) + ActiveSupport::NumberHelper.number_to_human(num, units: { thousand: 'k', million: 'M', billion: 'B' }) + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c3ca3be623f..aadde55bfe4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6778,72 +6778,6 @@ msgstr "" msgid "Cloud licenses can not be removed." msgstr "" -msgid "CloudLicense|Activate" -msgstr "" - -msgid "CloudLicense|Activate subscription" -msgstr "" - -msgid "CloudLicense|Activation code" -msgstr "" - -msgid "CloudLicense|Billable users" -msgstr "" - -msgid "CloudLicense|Buy subscription" -msgstr "" - -msgid "CloudLicense|Enter activation code" -msgstr "" - -msgid "CloudLicense|Free trial" -msgstr "" - -msgid "CloudLicense|Get help for the most common connectivity issues by %{linkStart}troubleshooting the activation code%{linkEnd}." -msgstr "" - -msgid "CloudLicense|I agree that my use of the GitLab Software is subject to the Subscription Agreement located at the %{linkStart}Terms of Service%{linkEnd}, unless otherwise agreed to in writing with GitLab." -msgstr "" - -msgid "CloudLicense|Maximum users" -msgstr "" - -msgid "CloudLicense|Paste your activation code" -msgstr "" - -msgid "CloudLicense|Ready to get started? A GitLab plan is ideal for scaling organizations and for multi team usage." -msgstr "" - -msgid "CloudLicense|Start free trial" -msgstr "" - -msgid "CloudLicense|Subscription" -msgstr "" - -msgid "CloudLicense|This is the highest peak of users on your installation since the license started." -msgstr "" - -msgid "CloudLicense|This is the number of %{billableUsersLinkStart}billable users%{billableUsersLinkEnd} on your installation, and this is the minimum number you need to purchase when you renew your license." -msgstr "" - -msgid "CloudLicense|To activate your subscription, connect to GitLab servers through the %{linkStart}Cloud Sync service%{linkEnd}, a hassle-free way to manage your subscription." -msgstr "" - -msgid "CloudLicense|Users in subscription" -msgstr "" - -msgid "CloudLicense|Users over subscription" -msgstr "" - -msgid "CloudLicense|Users with a Guest role or those who don't belong to a Project or Group will not use a seat from your license." -msgstr "" - -msgid "CloudLicense|You can start a free trial of GitLab Ultimate without any obligation or payment details." -msgstr "" - -msgid "CloudLicense|You'll be charged for %{trueUpLinkStart}users over license%{trueUpLinkEnd} on a quarterly or annual basis, depending on the terms of your agreement." -msgstr "" - msgid "Cluster" msgstr "" @@ -23973,6 +23907,9 @@ msgstr "" msgid "PerformanceBar|Gitaly calls" msgstr "" +msgid "PerformanceBar|Memory" +msgstr "" + msgid "PerformanceBar|Redis calls" msgstr "" @@ -31606,15 +31543,42 @@ msgstr "" msgid "Sunday" msgstr "" +msgid "SuperSonics|Activate" +msgstr "" + +msgid "SuperSonics|Activate subscription" +msgstr "" + msgid "SuperSonics|Activated on" msgstr "" +msgid "SuperSonics|Activation code" +msgstr "" + msgid "SuperSonics|An error occurred while activating your subscription." msgstr "" +msgid "SuperSonics|Billable users" +msgstr "" + +msgid "SuperSonics|Buy subscription" +msgstr "" + +msgid "SuperSonics|Enter activation code" +msgstr "" + msgid "SuperSonics|Expires on" msgstr "" +msgid "SuperSonics|Free trial" +msgstr "" + +msgid "SuperSonics|Get help for the most common connectivity issues by %{linkStart}troubleshooting the activation code%{linkEnd}." +msgstr "" + +msgid "SuperSonics|I agree that my use of the GitLab Software is subject to the Subscription Agreement located at the %{linkStart}Terms of Service%{linkEnd}, unless otherwise agreed to in writing with GitLab." +msgstr "" + msgid "SuperSonics|ID" msgstr "" @@ -31630,18 +31594,33 @@ msgstr "" msgid "SuperSonics|Manage" msgstr "" +msgid "SuperSonics|Maximum users" +msgstr "" + +msgid "SuperSonics|Paste your activation code" +msgstr "" + msgid "SuperSonics|Plan" msgstr "" +msgid "SuperSonics|Ready to get started? A GitLab plan is ideal for scaling organizations and for multi team usage." +msgstr "" + msgid "SuperSonics|Renews" msgstr "" msgid "SuperSonics|Seats" msgstr "" +msgid "SuperSonics|Start free trial" +msgstr "" + msgid "SuperSonics|Started" msgstr "" +msgid "SuperSonics|Subscription" +msgstr "" + msgid "SuperSonics|Subscription details" msgstr "" @@ -31657,21 +31636,45 @@ msgstr "" msgid "SuperSonics|This field is required." msgstr "" +msgid "SuperSonics|This is the highest peak of users on your installation since the license started." +msgstr "" + +msgid "SuperSonics|This is the number of %{billableUsersLinkStart}billable users%{billableUsersLinkEnd} on your installation, and this is the minimum number you need to purchase when you renew your license." +msgstr "" + +msgid "SuperSonics|To activate your subscription, connect to GitLab servers through the %{linkStart}Cloud Licensing%{linkEnd} service, a hassle-free way to manage your subscription." +msgstr "" + msgid "SuperSonics|Type" msgstr "" msgid "SuperSonics|Upload a legacy license" msgstr "" +msgid "SuperSonics|Users in subscription" +msgstr "" + +msgid "SuperSonics|Users over subscription" +msgstr "" + +msgid "SuperSonics|Users with a Guest role or those who don't belong to a Project or Group will not use a seat from your license." +msgstr "" + msgid "SuperSonics|Valid From" msgstr "" msgid "SuperSonics|You can no longer sync your subscription details with GitLab. Get help for the most common connectivity issues by %{connectivityHelpLinkStart}troubleshooting the activation code%{connectivityHelpLinkEnd}." msgstr "" +msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obligation or payment details." +msgstr "" + msgid "SuperSonics|You do not have an active subscription" msgstr "" +msgid "SuperSonics|You'll be charged for %{trueUpLinkStart}users over license%{trueUpLinkEnd} on a quarterly or annual basis, depending on the terms of your agreement." +msgstr "" + msgid "SuperSonics|Your subscription" msgstr "" diff --git a/package.json b/package.json index e5431b98689..2c2816edc98 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "codesandbox-api": "0.0.23", "compression-webpack-plugin": "^5.0.2", "copy-webpack-plugin": "^6.4.1", - "core-js": "^3.12.1", + "core-js": "^3.13.0", "cron-validator": "^1.1.1", "cropper": "^2.3.0", "css-loader": "^2.1.1", @@ -193,7 +193,7 @@ "@gitlab/eslint-plugin": "8.4.0", "@gitlab/stylelint-config": "2.3.0", "@testing-library/dom": "^7.16.2", - "@vue/test-utils": "1.1.2", + "@vue/test-utils": "1.2.0", "acorn": "^6.3.0", "axios-mock-adapter": "^1.15.0", "babel-jest": "^26.5.2", diff --git a/spec/frontend/__helpers__/vue_test_utils_helper.js b/spec/frontend/__helpers__/vue_test_utils_helper.js index a94cee84f74..2aae91f8a39 100644 --- a/spec/frontend/__helpers__/vue_test_utils_helper.js +++ b/spec/frontend/__helpers__/vue_test_utils_helper.js @@ -1,5 +1,5 @@ import * as testingLibrary from '@testing-library/dom'; -import { createWrapper, WrapperArray, mount, shallowMount } from '@vue/test-utils'; +import { createWrapper, WrapperArray, ErrorWrapper, mount, shallowMount } from '@vue/test-utils'; import { isArray, upperFirst } from 'lodash'; const vNodeContainsText = (vnode, text) => @@ -81,14 +81,9 @@ export const extendedWrapper = (wrapper) => { options, ); - // Return VTU `ErrorWrapper` if element is not found - // https://github.com/vuejs/vue-test-utils/blob/dev/packages/test-utils/src/error-wrapper.js - // VTU does not expose `ErrorWrapper` so, as of now, this is the best way to - // create an `ErrorWrapper` + // Element not found, return an `ErrorWrapper` if (!elements.length) { - const emptyElement = document.createElement('div'); - - return createWrapper(emptyElement).find('testing-library-element-not-found'); + return new ErrorWrapper(query); } return createWrapper(elements[0], this.options || {}); diff --git a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js index dfe5a483223..3bb228f94b8 100644 --- a/spec/frontend/__helpers__/vue_test_utils_helper_spec.js +++ b/spec/frontend/__helpers__/vue_test_utils_helper_spec.js @@ -4,6 +4,7 @@ import { shallowMount, Wrapper as VTUWrapper, WrapperArray as VTUWrapperArray, + ErrorWrapper as VTUErrorWrapper, } from '@vue/test-utils'; import { extendedWrapper, @@ -195,7 +196,7 @@ describe('Vue test utils helpers', () => { }); it('returns a VTU error wrapper', () => { - expect(wrapper[findMethod](text, options).exists()).toBe(false); + expect(wrapper[findMethod](text, options)).toBeInstanceOf(VTUErrorWrapper); }); }); }); diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js index 646e51160d8..41a93c7ba5a 100644 --- a/spec/frontend/ide/components/repo_editor_spec.js +++ b/spec/frontend/ide/components/repo_editor_spec.js @@ -656,7 +656,7 @@ describe('RepoEditor', () => { }); it("does not add file to state or set markdown image syntax if the file isn't markdown", async () => { - wrapper.setProps({ + await wrapper.setProps({ file: setFileName('myfile.txt'), }); pasteImage(); diff --git a/spec/frontend/notes/components/discussion_actions_spec.js b/spec/frontend/notes/components/discussion_actions_spec.js index c6a7d7ead98..925dbcc09ec 100644 --- a/spec/frontend/notes/components/discussion_actions_spec.js +++ b/spec/frontend/notes/components/discussion_actions_spec.js @@ -20,7 +20,7 @@ const createUnallowedNote = () => describe('DiscussionActions', () => { let wrapper; - const createComponentFactory = (shallow = true) => (props) => { + const createComponentFactory = (shallow = true) => (props, options) => { const store = createStore(); const mountFn = shallow ? shallowMount : mount; @@ -34,6 +34,7 @@ describe('DiscussionActions', () => { shouldShowJumpToNextDiscussion: true, ...props, }, + ...options, }); }; @@ -90,17 +91,17 @@ describe('DiscussionActions', () => { describe('events handling', () => { const createComponent = createComponentFactory(false); - beforeEach(() => { - createComponent(); - }); - it('emits showReplyForm event when clicking on reply placeholder', () => { + createComponent({}, { attachTo: document.body }); + jest.spyOn(wrapper.vm, '$emit'); wrapper.find(ReplyPlaceholder).find('textarea').trigger('focus'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('showReplyForm'); }); it('emits resolve event when clicking on resolve button', () => { + createComponent(); + jest.spyOn(wrapper.vm, '$emit'); wrapper.find(ResolveDiscussionButton).find('button').trigger('click'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('resolve'); diff --git a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js index 2a4cd0df0c7..3932f818c4e 100644 --- a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js +++ b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js @@ -6,31 +6,34 @@ const placeholderText = 'Test Button Text'; describe('ReplyPlaceholder', () => { let wrapper; - const findTextarea = () => wrapper.find({ ref: 'textarea' }); - - beforeEach(() => { + const createComponent = ({ options = {} } = {}) => { wrapper = shallowMount(ReplyPlaceholder, { propsData: { placeholderText, }, + ...options, }); - }); + }; + + const findTextarea = () => wrapper.find({ ref: 'textarea' }); afterEach(() => { wrapper.destroy(); }); - it('emits focus event on button click', () => { - findTextarea().trigger('focus'); + it('emits focus event on button click', async () => { + createComponent({ options: { attachTo: document.body } }); + + await findTextarea().trigger('focus'); - return wrapper.vm.$nextTick().then(() => { - expect(wrapper.emitted()).toEqual({ - focus: [[]], - }); + expect(wrapper.emitted()).toEqual({ + focus: [[]], }); }); it('should render reply button', () => { + createComponent(); + expect(findTextarea().attributes('placeholder')).toEqual(placeholderText); }); }); diff --git a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap index c4c48ea7517..4ba9120d196 100644 --- a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap +++ b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap @@ -66,7 +66,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`] <gl-area-chart-stub annotations="" data="[object Object]" - formattooltiptext="function () { [native code] }" + formattooltiptext="[Function]" height="200" includelegendavgmax="true" legendaveragetext="Avg" diff --git a/spec/lib/peek/views/memory_spec.rb b/spec/lib/peek/views/memory_spec.rb new file mode 100644 index 00000000000..1f88aadfc54 --- /dev/null +++ b/spec/lib/peek/views/memory_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Peek::Views::Memory, :request_store do + subject! { described_class.new } + + before do + stub_memory_instrumentation + end + + context 'with process_action.action_controller notification' do + it 'returns empty results when it has not yet fired' do + expect(subject.results).to eq({}) + end + + it 'returns memory instrumentation data when it has fired' do + publish_notification + + expect(subject.results[:calls]).to eq('2 MB') + expect(subject.results[:details]).to all(have_key(:item_header)) + expect(subject.results[:details]).to all(have_key(:item_content)) + expect(subject.results[:summary]).to include('Objects allocated' => '200 k') + expect(subject.results[:summary]).to include('Allocator calls' => '500') + expect(subject.results[:summary]).to include('Large allocations' => '1 KB') + end + end + + def stub_memory_instrumentation + start_memory = { + total_malloc_bytes: 1, + total_mallocs: 2, + total_allocated_objects: 3 + } + allow(Gitlab::Memory::Instrumentation).to receive(:start_thread_memory_allocations).and_return(start_memory) + allow(Gitlab::Memory::Instrumentation).to receive(:measure_thread_memory_allocations).with(start_memory).and_return({ + mem_total_bytes: 2_097_152, + mem_bytes: 1024, + mem_mallocs: 500, + mem_objects: 200_000 + }) + Gitlab::InstrumentationHelper.init_instrumentation_data + end + + def publish_notification + headers = double + allow(headers).to receive(:env).and_return('action_dispatch.request_id': 'req-42') + + ActiveSupport::Notifications.publish( + 'process_action.action_controller', Time.current - 1.second, Time.current, 'id', headers: headers + ) + end +end diff --git a/yarn.lock b/yarn.lock index 851d6618866..a0961053bbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1816,10 +1816,10 @@ source-map "~0.6.1" vue-template-es2015-compiler "^1.9.0" -"@vue/test-utils@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.1.2.tgz#fdb487448dceefeaf3d01d465f7c836a3d666dbc" - integrity sha512-utbIL7zn9c+SjhybPwh48lpWCiluFCbP1yyRNAy1fQsw/6hiNFioaWy05FoVAFIZXC5WwBf+5r4ypfM1j/nI4A== +"@vue/test-utils@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.2.0.tgz#3bc8c17ed549157275f0aec6b95da40887f7297f" + integrity sha512-poBTLqeJYNq1TXVhtVfnY8vELUVOFdJY8KZZoUuaAkIqPTWsxonU1M8nMWpZT+xEMrM+49+YcuEqtMHVD9Q9gw== dependencies: dom-event-types "^1.0.0" lodash "^4.17.15" @@ -3606,10 +3606,10 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-js@^3.1.3, core-js@^3.12.1: - version "3.12.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" - integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw== +core-js@^3.1.3, core-js@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.13.0.tgz#58ca436bf01d6903aee3d364089868d0d89fe58d" + integrity sha512-iWDbiyha1M5vFwPFmQnvRv+tJzGbFAm6XimJUT0NgHYW3xZEs1SkCAcasWSVFxpI2Xb/V1DDJckq3v90+bQnog== core-js@~2.3.0: version "2.3.0" |