Commit 1439354d authored by Tiger Watson's avatar Tiger Watson

Merge branch 'fix-slow-uncached-query-for-vsa-label-stages' into 'master'

Fix slow uncached VSA label stage query

See merge request gitlab-org/gitlab!38252
parents 3eb1947f e5adc309
# frozen_string_literal: true
class AddIndexesToResourceLabelEventsToSupportVsa < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
NEW_INDEX_NAME_ON_ISSUE_ID = 'index_resource_label_events_issue_id_label_id_action'
OLD_INDEX_NAME_ON_ISSUE_ID = 'index_resource_label_events_on_issue_id'
NEW_INDEX_NAME_ON_MERGE_REQUEST_ID = 'index_resource_label_events_on_merge_request_id_label_id_action'
OLD_INDEX_NAME_ON_MERGE_REQUEST_ID = 'index_resource_label_events_on_merge_request_id'
def up
add_concurrent_index :resource_label_events, [:issue_id, :label_id, :action], name: NEW_INDEX_NAME_ON_ISSUE_ID
remove_concurrent_index_by_name :resource_label_events, OLD_INDEX_NAME_ON_ISSUE_ID
add_concurrent_index :resource_label_events, [:merge_request_id, :label_id, :action], name: NEW_INDEX_NAME_ON_MERGE_REQUEST_ID
remove_concurrent_index_by_name :resource_label_events, OLD_INDEX_NAME_ON_MERGE_REQUEST_ID
end
def down
add_concurrent_index :resource_label_events, :issue_id, name: OLD_INDEX_NAME_ON_ISSUE_ID
remove_concurrent_index_by_name(:resource_label_events, NEW_INDEX_NAME_ON_ISSUE_ID)
add_concurrent_index :resource_label_events, :merge_request_id, name: OLD_INDEX_NAME_ON_MERGE_REQUEST_ID
remove_concurrent_index_by_name(:resource_label_events, NEW_INDEX_NAME_ON_MERGE_REQUEST_ID)
end
end
406594bc48558c3ad50680c7e1fa795f38abb92696acbb94ae2dfb13d8dcaf1a
\ No newline at end of file
...@@ -20435,13 +20435,13 @@ CREATE INDEX index_requirements_on_title_trigram ON public.requirements USING gi ...@@ -20435,13 +20435,13 @@ CREATE INDEX index_requirements_on_title_trigram ON public.requirements USING gi
CREATE INDEX index_requirements_on_updated_at ON public.requirements USING btree (updated_at); CREATE INDEX index_requirements_on_updated_at ON public.requirements USING btree (updated_at);
CREATE INDEX index_resource_label_events_on_epic_id ON public.resource_label_events USING btree (epic_id); CREATE INDEX index_resource_label_events_issue_id_label_id_action ON public.resource_label_events USING btree (issue_id, label_id, action);
CREATE INDEX index_resource_label_events_on_issue_id ON public.resource_label_events USING btree (issue_id); CREATE INDEX index_resource_label_events_on_epic_id ON public.resource_label_events USING btree (epic_id);
CREATE INDEX index_resource_label_events_on_label_id_and_action ON public.resource_label_events USING btree (label_id, action); CREATE INDEX index_resource_label_events_on_label_id_and_action ON public.resource_label_events USING btree (label_id, action);
CREATE INDEX index_resource_label_events_on_merge_request_id ON public.resource_label_events USING btree (merge_request_id); CREATE INDEX index_resource_label_events_on_merge_request_id_label_id_action ON public.resource_label_events USING btree (merge_request_id, label_id, action);
CREATE INDEX index_resource_label_events_on_user_id ON public.resource_label_events USING btree (user_id); CREATE INDEX index_resource_label_events_on_user_id ON public.resource_label_events USING btree (user_id);
......
---
title: Fix slow Value Stream label stage query
merge_request: 38252
author:
type: performance
...@@ -32,8 +32,8 @@ module Gitlab ...@@ -32,8 +32,8 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query) def apply_query_customization(query)
query query
.joins("INNER JOIN (#{subquery.to_sql}) #{join_expression_name} on #{join_expression_name}.model_id = #{quote_table_name(object_type.table_name)}.id") .from(Arel::Nodes::Grouping.new(Arel.sql(object_type.all.to_sql)).as(object_type.table_name)) # This is needed for the LATERAL JOIN: FROM (SELECT * FROM table) as table
.where("#{join_expression_name}.label_assignment_order = 1") .joins("INNER JOIN LATERAL (#{subquery.to_sql}) #{join_expression_name} ON TRUE")
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -58,11 +58,6 @@ module Gitlab ...@@ -58,11 +58,6 @@ module Gitlab
# - IssueLabelAdded event: find the first assignment (add, id = 1) # - IssueLabelAdded event: find the first assignment (add, id = 1)
# - IssueLabelRemoved event: find the latest unassignment (remove, id = 4) # - IssueLabelRemoved event: find the latest unassignment (remove, id = 4)
# #
# This can be achieved with the PARTITION window function.
#
# - IssueLabelAdded: order by `created_at` ASC and take the row number 1
# - IssueLabelRemoved: order by `created_at` DESC and take the row number 1
#
# Arguments: # Arguments:
# foreign_key: :issue_id or :merge_request_id (based on resource_label_events table) # foreign_key: :issue_id or :merge_request_id (based on resource_label_events table)
# label: label model, # label: label model,
...@@ -72,9 +67,12 @@ module Gitlab ...@@ -72,9 +67,12 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def resource_label_events_with_subquery(foreign_key, label, action, order) def resource_label_events_with_subquery(foreign_key, label, action, order)
ResourceLabelEvent ResourceLabelEvent
.select(:created_at, resource_label_events_table[foreign_key].as('model_id'), partition_select(foreign_key, order).as('label_assignment_order')) .select(:created_at)
.where(action: action) .where(action: action)
.where(label_id: label.id) .where(label_id: label.id)
.where(ResourceLabelEvent.arel_table[foreign_key].eq(object_type.arel_table[:id]))
.order(order_expression(order))
.limit(1)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -83,9 +81,8 @@ module Gitlab ...@@ -83,9 +81,8 @@ module Gitlab
@join_expression_name ||= quote_table_name("#{self.class.to_s.demodulize.underscore}_#{SecureRandom.hex(5)}") @join_expression_name ||= quote_table_name("#{self.class.to_s.demodulize.underscore}_#{SecureRandom.hex(5)}")
end end
# rubocop: disable CodeReuse/ActiveRecord def order_expression(order)
def partition_select(foreign_key, order) case order
order_expression = case order
when :asc when :asc
resource_label_events_table[:created_at].asc resource_label_events_table[:created_at].asc
when :desc when :desc
...@@ -93,13 +90,7 @@ module Gitlab ...@@ -93,13 +90,7 @@ module Gitlab
else else
raise "unsupported order option: #{order}" raise "unsupported order option: #{order}"
end end
Arel::Nodes::Over.new(
Arel::Nodes::NamedFunction.new('row_number', []),
Arel::Nodes::Window.new.partition(resource_label_events_table[foreign_key]).order(order_expression)
)
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -90,7 +90,7 @@ module Gitlab ...@@ -90,7 +90,7 @@ module Gitlab
end end
def ordered_and_limited_query def ordered_and_limited_query
order_by_end_event(query).limit(MAX_RECORDS) order_by_end_event(query, columns).limit(MAX_RECORDS)
end end
def records def records
......
...@@ -24,13 +24,13 @@ module Gitlab ...@@ -24,13 +24,13 @@ module Gitlab
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def order_by_end_event(query) def order_by_end_event(query, extra_columns_to_select = [:id])
ordered_query = query.reorder(stage.end_event.timestamp_projection.desc) ordered_query = query.reorder(stage.end_event.timestamp_projection.desc)
# When filtering for more than one label, postgres requires the columns in ORDER BY to be present in the GROUP BY clause # When filtering for more than one label, postgres requires the columns in ORDER BY to be present in the GROUP BY clause
if requires_grouping? if requires_grouping?
column_list = [ column_list = [
ordered_query.arel_table[:id], *extra_columns_to_select,
*stage.end_event.column_list, *stage.end_event.column_list,
*stage.start_event.column_list *stage.start_event.column_list
] ]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment