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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 00:06:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 00:06:01 +0300
commitf607152a0802a68067343ad73f989033cb8e9a06 (patch)
treebbc16fd5f827ea5e30527d455a01dd6b1249a19c /lib
parent7c862041c66833ebf49d9964f1797d93b1829690 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/analytics/cycle_analytics/base_query_builder.rb70
-rw-r--r--lib/gitlab/analytics/cycle_analytics/data_collector.rb42
-rw-r--r--lib/gitlab/analytics/cycle_analytics/default_stages.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/median.rb39
-rw-r--r--lib/gitlab/analytics/cycle_analytics/records_fetcher.rb132
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb28
-rw-r--r--lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml29
7 files changed, 342 insertions, 2 deletions
diff --git a/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb b/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb
new file mode 100644
index 00000000000..33cbe1a62ef
--- /dev/null
+++ b/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ module CycleAnalytics
+ class BaseQueryBuilder
+ include Gitlab::CycleAnalytics::MetricsTables
+
+ delegate :subject_class, to: :stage
+
+ # rubocop: disable CodeReuse/ActiveRecord
+
+ def initialize(stage:, params: {})
+ @stage = stage
+ @params = params
+ end
+
+ def build
+ query = subject_class
+ query = filter_by_parent_model(query)
+ query = filter_by_time_range(query)
+ query = stage.start_event.apply_query_customization(query)
+ query = stage.end_event.apply_query_customization(query)
+ query.where(duration_condition)
+ end
+
+ private
+
+ attr_reader :stage, :params
+
+ def duration_condition
+ stage.end_event.timestamp_projection.gteq(stage.start_event.timestamp_projection)
+ end
+
+ def filter_by_parent_model(query)
+ if parent_class.eql?(Project)
+ if subject_class.eql?(Issue)
+ query.where(project_id: stage.parent_id)
+ elsif subject_class.eql?(MergeRequest)
+ query.where(target_project_id: stage.parent_id)
+ else
+ raise ArgumentError, "unknown subject_class: #{subject_class}"
+ end
+ else
+ raise ArgumentError, "unknown parent_class: #{parent_class}"
+ end
+ end
+
+ def filter_by_time_range(query)
+ from = params.fetch(:from, 30.days.ago)
+ to = params[:to]
+
+ query = query.where(subject_table[:created_at].gteq(from))
+ query = query.where(subject_table[:created_at].lteq(to)) if to
+ query
+ end
+
+ def subject_table
+ subject_class.arel_table
+ end
+
+ def parent_class
+ stage.parent.class
+ end
+
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/analytics/cycle_analytics/data_collector.rb b/lib/gitlab/analytics/cycle_analytics/data_collector.rb
new file mode 100644
index 00000000000..0c0f737f2c9
--- /dev/null
+++ b/lib/gitlab/analytics/cycle_analytics/data_collector.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ module CycleAnalytics
+ # Arguments:
+ # stage - an instance of CycleAnalytics::ProjectStage or CycleAnalytics::GroupStage
+ # params:
+ # current_user: an instance of User
+ # from: DateTime
+ # to: DateTime
+ class DataCollector
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(stage:, params: {})
+ @stage = stage
+ @params = params
+ end
+
+ def records_fetcher
+ strong_memoize(:records_fetcher) do
+ RecordsFetcher.new(stage: stage, query: query, params: params)
+ end
+ end
+
+ def median
+ strong_memoize(:median) do
+ Median.new(stage: stage, query: query)
+ end
+ end
+
+ private
+
+ attr_reader :stage, :params
+
+ def query
+ BaseQueryBuilder.new(stage: stage, params: params).build
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/analytics/cycle_analytics/default_stages.rb b/lib/gitlab/analytics/cycle_analytics/default_stages.rb
index 711645800fb..8e70236ce75 100644
--- a/lib/gitlab/analytics/cycle_analytics/default_stages.rb
+++ b/lib/gitlab/analytics/cycle_analytics/default_stages.rb
@@ -92,8 +92,8 @@ module Gitlab
name: 'production',
custom: false,
relative_position: 7,
- start_event_identifier: :merge_request_merged,
- end_event_identifier: :merge_request_first_deployed_to_production
+ start_event_identifier: :issue_created,
+ end_event_identifier: :production_stage_end
}
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/median.rb b/lib/gitlab/analytics/cycle_analytics/median.rb
new file mode 100644
index 00000000000..41883a80338
--- /dev/null
+++ b/lib/gitlab/analytics/cycle_analytics/median.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ module CycleAnalytics
+ class Median
+ include StageQueryHelpers
+
+ def initialize(stage:, query:)
+ @stage = stage
+ @query = query
+ end
+
+ def seconds
+ @query = @query.select(median_duration_in_seconds.as('median'))
+ result = execute_query(@query).first || {}
+
+ result['median'] ? result['median'].to_i : nil
+ end
+
+ private
+
+ attr_reader :stage
+
+ def percentile_cont
+ percentile_cont_ordering = Arel::Nodes::UnaryOperation.new(Arel::Nodes::SqlLiteral.new('ORDER BY'), duration)
+ Arel::Nodes::NamedFunction.new(
+ 'percentile_cont(0.5) WITHIN GROUP',
+ [percentile_cont_ordering]
+ )
+ end
+
+ def median_duration_in_seconds
+ Arel::Nodes::Extract.new(percentile_cont, :epoch)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
new file mode 100644
index 00000000000..90d03142b2a
--- /dev/null
+++ b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ module CycleAnalytics
+ class RecordsFetcher
+ include Gitlab::Utils::StrongMemoize
+ include StageQueryHelpers
+ include Gitlab::CycleAnalytics::MetricsTables
+
+ MAX_RECORDS = 20
+
+ MAPPINGS = {
+ Issue => {
+ finder_class: IssuesFinder,
+ serializer_class: AnalyticsIssueSerializer,
+ includes_for_query: { project: [:namespace], author: [] },
+ columns_for_select: %I[title iid id created_at author_id project_id]
+ },
+ MergeRequest => {
+ finder_class: MergeRequestsFinder,
+ serializer_class: AnalyticsMergeRequestSerializer,
+ includes_for_query: { target_project: [:namespace], author: [] },
+ columns_for_select: %I[title iid id created_at author_id state target_project_id]
+ }
+ }.freeze
+
+ delegate :subject_class, to: :stage
+
+ def initialize(stage:, query:, params: {})
+ @stage = stage
+ @query = query
+ @params = params
+ end
+
+ def serialized_records
+ strong_memoize(:serialized_records) do
+ # special case (legacy): 'Test' and 'Staging' stages should show Ci::Build records
+ if default_test_stage? || default_staging_stage?
+ AnalyticsBuildSerializer.new.represent(ci_build_records.map { |e| e['build'] })
+ else
+ records.map do |record|
+ project = record.project
+ attributes = record.attributes.merge({
+ project_path: project.path,
+ namespace_path: project.namespace.path,
+ author: record.author
+ })
+ serializer.represent(attributes)
+ end
+ end
+ end
+ end
+
+ private
+
+ attr_reader :stage, :query, :params
+
+ def finder_query
+ MAPPINGS
+ .fetch(subject_class)
+ .fetch(:finder_class)
+ .new(params.fetch(:current_user), finder_params.fetch(stage.parent.class))
+ .execute
+ end
+
+ def columns
+ MAPPINGS.fetch(subject_class).fetch(:columns_for_select).map do |column_name|
+ subject_class.arel_table[column_name]
+ end
+ end
+
+ # EE will override this to include Group rules
+ def finder_params
+ {
+ Project => { project_id: stage.parent_id }
+ }
+ end
+
+ def default_test_stage?
+ stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_test_stage)
+ end
+
+ def default_staging_stage?
+ stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_staging_stage)
+ end
+
+ def serializer
+ MAPPINGS.fetch(subject_class).fetch(:serializer_class).new
+ end
+
+ # Loading Ci::Build records instead of MergeRequest records
+ # rubocop: disable CodeReuse/ActiveRecord
+ def ci_build_records
+ ci_build_join = mr_metrics_table
+ .join(build_table)
+ .on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
+ .join_sources
+
+ q = ordered_and_limited_query
+ .joins(ci_build_join)
+ .select(build_table[:id], round_duration_to_seconds.as('total_time'))
+
+ results = execute_query(q).to_a
+
+ Gitlab::CycleAnalytics::Updater.update!(results, from: 'id', to: 'build', klass: ::Ci::Build.includes({ project: [:namespace], user: [], pipeline: [] }))
+ end
+
+ def ordered_and_limited_query
+ query
+ .reorder(stage.end_event.timestamp_projection.desc)
+ .limit(MAX_RECORDS)
+ end
+
+ def records
+ results = finder_query
+ .merge(ordered_and_limited_query)
+ .select(*columns, round_duration_to_seconds.as('total_time'))
+
+ # using preloader instead of includes to avoid AR generating a large column list
+ ActiveRecord::Associations::Preloader.new.preload(
+ results,
+ MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
+ )
+
+ results
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb b/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb
new file mode 100644
index 00000000000..34c726b2254
--- /dev/null
+++ b/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ module CycleAnalytics
+ module StageQueryHelpers
+ def execute_query(query)
+ ActiveRecord::Base.connection.execute(query.to_sql)
+ end
+
+ def zero_interval
+ Arel::Nodes::NamedFunction.new("CAST", [Arel.sql("'0' AS INTERVAL")])
+ end
+
+ def round_duration_to_seconds
+ Arel::Nodes::Extract.new(duration, :epoch)
+ end
+
+ def duration
+ Arel::Nodes::Subtraction.new(
+ stage.end_event.timestamp_projection,
+ stage.start_event.timestamp_projection
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
new file mode 100644
index 00000000000..eced181e966
--- /dev/null
+++ b/lib/gitlab/ci/templates/Verify/Browser-Performance.gitlab-ci.yml
@@ -0,0 +1,29 @@
+# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
+
+stages:
+ - build
+ - test
+ - deploy
+ - performance
+
+performance:
+ stage: performance
+ image: docker:git
+ variables:
+ URL: https://example.com
+ SITESPEED_VERSION: 6.3.1
+ SITESPEED_OPTIONS: ''
+ services:
+ - docker:stable-dind
+ script:
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL $SITESPEED_OPTIONS
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - performance.json
+ - sitespeed-results/
+ reports:
+ performance: performance.json