Skip to content
Snippets Groups Projects
Commit 982d5a05 authored by James Lopez's avatar James Lopez
Browse files

refactored metrics fetcher - merged into stage and events

parent 834bcacb
No related branches found
No related tags found
No related merge requests found
Showing
with 119 additions and 121 deletions
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class BaseEventFetcher class BaseEventFetcher
include MetricsTables include BaseQuery
   
attr_reader :projections, :query, :stage, :order attr_reader :projections, :query, :stage, :order
   
def initialize(fetcher:, options:, stage:) def initialize(project:, stage:, options:)
@fetcher = fetcher @project = project
@project = fetcher.project
@options = options
@stage = stage @stage = stage
@options = options
end end
   
def fetch def fetch
Loading
@@ -20,8 +19,6 @@ module Gitlab
Loading
@@ -20,8 +19,6 @@ module Gitlab
end.compact end.compact
end end
   
def custom_query(_base_query); end
private private
   
def update_author! def update_author!
Loading
@@ -31,7 +28,21 @@ module Gitlab
Loading
@@ -31,7 +28,21 @@ module Gitlab
end end
   
def event_result def event_result
@event_result ||= @fetcher.events.to_a @event_result ||= ActiveRecord::Base.connection.exec_query(events_query.to_sql).to_a
end
def events_query
diff_fn = subtract_datetimes_diff(base_query, @options[:start_time_attrs], @options[:end_time_attrs])
base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *projections).order(order.desc)
end
def order
@order || default_order
end
def default_order
@options[:start_time_attrs].is_a?(Array) ? @options[:start_time_attrs].first : @options[:start_time_attrs]
end end
   
def serialize(_event) def serialize(_event)
Loading
Loading
module Gitlab
module CycleAnalytics
module BaseQuery
include MetricsTables
include Gitlab::Database::Median
include Gitlab::Database::DateTime
private
def base_query
@base_query ||= stage_query
end
def stage_query
query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])).
join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])).
where(issue_table[:project_id].eq(@project.id)).
where(issue_table[:deleted_at].eq(nil)).
where(issue_table[:created_at].gteq(@options[:from]))
# Load merge_requests
query = query.join(mr_table, Arel::Nodes::OuterJoin).
on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])).
join(mr_metrics_table).
on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
query
end
end
end
end
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class BaseStage class BaseStage
include MetricsTables include BaseQuery
attr_accessor :start_time_attrs, :end_time_attrs
   
def initialize(project:, options:) def initialize(project:, options:)
@project = project @project = project
@options = options @options = options
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project,
from: options[:from],
branch: options[:branch],
stage: self)
end end
   
def event def event
@event ||= Gitlab::CycleAnalytics::Event[stage].new(fetcher: @fetcher, @event ||= Gitlab::CycleAnalytics::Event[name].new(project: @project,
options: @options, stage: name,
stage: stage) options: event_options)
end end
   
def events def events
Loading
@@ -29,17 +23,31 @@ module Gitlab
Loading
@@ -29,17 +23,31 @@ module Gitlab
end end
   
def title def title
stage.to_s.capitalize name.to_s.capitalize
end end
   
def median def median
@fetcher.median cte_table = Arel::Table.new("cte_table_for_#{name}")
# Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time).
# Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time).
# We compute the (end_time - start_time) interval, and give it an alias based on the current
# cycle analytics stage.
interval_query = Arel::Nodes::As.new(
cte_table,
subtract_datetimes(base_query, @start_time_attrs, @end_time_attrs, name.to_s))
median_datetime(cte_table, interval_query, name)
end
def name
raise NotImplementedError.new("Expected #{self.name} to implement name")
end end
   
private private
   
def stage def event_options
class_name_for('Stage') @options.merge(start_time_attrs: @start_time_attrs, end_time_attrs: @end_time_attrs)
end end
end end
end end
Loading
Loading
Loading
@@ -8,7 +8,7 @@ module Gitlab
Loading
@@ -8,7 +8,7 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:code :code
end end
   
Loading
Loading
Loading
@@ -9,7 +9,7 @@ module Gitlab
Loading
@@ -9,7 +9,7 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:issue :issue
end end
   
Loading
Loading
module Gitlab
module CycleAnalytics
class MetricsFetcher
include Gitlab::Database::Median
include Gitlab::Database::DateTime
include MetricsTables
attr_reader :project
DEPLOYMENT_METRIC_STAGES = %i[production staging]
def initialize(project:, from:, branch:, stage:)
@project = project
@from = from
@branch = branch
@stage = stage
end
def median
cte_table = Arel::Table.new("cte_table_for_#{@stage.stage}")
# Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time).
# Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time).
# We compute the (end_time - start_time) interval, and give it an alias based on the current
# cycle analytics stage.
interval_query = Arel::Nodes::As.new(
cte_table,
subtract_datetimes(base_query_for(@stage.stage), @stage.start_time_attrs, @stage.end_time_attrs, @stage.stage.to_s))
median_datetime(cte_table, interval_query, @stage.stage)
end
def events
ActiveRecord::Base.connection.exec_query(events_query.to_sql)
end
private
def events_query
base_query = base_query_for(@stage.stage)
diff_fn = subtract_datetimes_diff(base_query, @stage.start_time_attrs, @stage.end_time_attrs)
@stage.event.custom_query(base_query)
base_query.project(extract_diff_epoch(diff_fn).as('total_time'), *@stage.event.projections).order(order.desc)
end
def order
@stage.event.order || default_order
end
def default_order
@stage.start_time_attrs.is_a?(Array) ? @stage.start_time_attrs.first : @stage.start_time_attrs
end
# Join table with a row for every <issue,merge_request> pair (where the merge request
# closes the given issue) with issue and merge request metrics included. The metrics
# are loaded with an inner join, so issues / merge requests without metrics are
# automatically excluded.
def base_query_for(name)
# Load issues
query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])).
join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])).
where(issue_table[:project_id].eq(@project.id)).
where(issue_table[:deleted_at].eq(nil)).
where(issue_table[:created_at].gteq(@from))
query = query.where(build_table[:ref].eq(@branch)) if name == :test && @branch
# Load merge_requests
query = query.join(mr_table, Arel::Nodes::OuterJoin).
on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])).
join(mr_metrics_table).
on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
if DEPLOYMENT_METRIC_STAGES.include?(name)
# Limit to merge requests that have been deployed to production after `@from`
query.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@from))
end
query
end
end
end
end
Loading
@@ -8,8 +8,10 @@ module Gitlab
Loading
@@ -8,8 +8,10 @@ module Gitlab
super(*args) super(*args)
end end
   
def custom_query(base_query) def events_query
base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id]))
super
end end
   
private private
Loading
Loading
Loading
@@ -9,7 +9,7 @@ module Gitlab
Loading
@@ -9,7 +9,7 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:plan :plan
end end
   
Loading
Loading
module Gitlab
module CycleAnalytics
module ProductionHelper
def stage_query
super.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@options[:from]))
end
end
end
end
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class ProductionStage < BaseStage class ProductionStage < BaseStage
include ProductionHelper
def initialize(*args) def initialize(*args)
@start_time_attrs = issue_table[:created_at] @start_time_attrs = issue_table[:created_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
Loading
@@ -8,13 +10,18 @@ module Gitlab
Loading
@@ -8,13 +10,18 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:production :production
end end
   
def description def description
"From issue creation until deploy to production" "From issue creation until deploy to production"
end end
def query
# Limit to merge requests that have been deployed to production after `@from`
query.where(mr_metrics_table[:first_deployed_to_production_at].gteq(@from))
end
end end
end end
end end
Loading
@@ -8,7 +8,7 @@ module Gitlab
Loading
@@ -8,7 +8,7 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:review :review
end end
   
Loading
Loading
Loading
@@ -14,8 +14,10 @@ module Gitlab
Loading
@@ -14,8 +14,10 @@ module Gitlab
super super
end end
   
def custom_query(base_query) def events_query
base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
super
end end
   
private private
Loading
Loading
module Gitlab module Gitlab
module CycleAnalytics module CycleAnalytics
class StagingStage < BaseStage class StagingStage < BaseStage
include ProductionHelper
def initialize(*args) def initialize(*args)
@start_time_attrs = mr_metrics_table[:merged_at] @start_time_attrs = mr_metrics_table[:merged_at]
@end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at]
Loading
@@ -8,7 +10,7 @@ module Gitlab
Loading
@@ -8,7 +10,7 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:staging :staging
end end
   
Loading
Loading
Loading
@@ -23,8 +23,11 @@ module Gitlab
Loading
@@ -23,8 +23,11 @@ module Gitlab
cmd << "--after=#{@from.iso8601}" cmd << "--after=#{@from.iso8601}"
cmd << sha cmd << sha
   
raw_output = IO.popen(cmd) { |io| io.read } output, status = Gitlab::Popen.popen(cmd) { |io| io.read }
raw_output.lines.count
raise IOError, output unless status.zero?
output.lines.count
end end
   
def ref def ref
Loading
Loading
Loading
@@ -8,13 +8,21 @@ module Gitlab
Loading
@@ -8,13 +8,21 @@ module Gitlab
super(*args) super(*args)
end end
   
def stage def name
:test :test
end end
   
def description def description
"Total test time for all commits/merges" "Total test time for all commits/merges"
end end
def stage_query
if @options[:branch]
super.where(build_table[:ref].eq(@options[:branch]))
else
super
end
end
end end
end end
end end
Loading
@@ -8,10 +8,11 @@ shared_examples 'default query config' do
Loading
@@ -8,10 +8,11 @@ shared_examples 'default query config' do
stage: stage_name) stage: stage_name)
end end
   
let(:event) { described_class.new(fetcher: fetcher, options: {}, stage: stage_name) } let(project)
let(:event) { described_class.new(project: project, stage: stage_name, options: {}) }
   
it 'has the stage attribute' do it 'has the stage attribute' do
expect(event.stage).not_to be_nil expect(event.name).not_to be_nil
end end
   
it 'has the projection attributes' do it 'has the projection attributes' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment