Constrain environment deployments to project IDs

When querying the deployments of an environment the query Rails produces
will be along the lines of the following:

    SELECT *
    FROM deployments
    WHERE environment_id = X

For queries such as this (or queries that use this as their base and add
more conditions) there is no meaningful index that can be used as long
as deployments.project_id is not part of a WHERE clause.

To work around this we change that "has_many :deployments" relation to
always add a "WHERE project_id = X" condition. This means that queries
filtering deployments can make better use of the existing indexes. For
example, when filtering by deployments.iid this will result in the
following query:

    SELECT *
    FROM deployments
    WHERE environment_id = X
    AND project_id = Y
    AND iid = Z

This means PostgreSQL can use the existing index on
(project_id, environment_id, iid) instead of having to use a different
index (or none at all) and having to scan over a large amount of data.

Query plan wise this means that instead of this query and plan:

    EXPLAIN (BUFFERS, ANALYZE)
    SELECT deployments.*
    FROM deployments
    WHERE deployments.environment_id = 5
    AND deployments.iid = 225;

    Index Scan using index_deployments_on_project_id_and_iid on deployments  (cost=0.42..14465.75 rows=1 width=117) (actual time=6.394..38.048 rows=1 loops=1)
      Index Cond: (iid = 225)
      Filter: (environment_id = 5)
      Rows Removed by Filter: 839
      Buffers: shared hit=4534
    Planning time: 0.076 ms
    Execution time: 38.073 ms

We produce the following query and plan:

    EXPLAIN (BUFFERS, ANALYZE)
    SELECT deployments.*
    FROM deployments
    WHERE deployments.environment_id = 5
    AND deployments.iid = 225
    AND deployments.project_id = 1292351;

    Index Scan using index_deployments_on_project_id_and_iid on deployments  (cost=0.42..4.45 rows=1 width=117) (actual time=0.018..0.018 rows=1 loops=1)
      Index Cond: ((project_id = 1292351) AND (iid = 225))
      Filter: (environment_id = 5)
      Buffers: shared hit=4
    Planning time: 0.088 ms
    Execution time: 0.039 ms

On GitLab.com these changes result in a (roughly) 11x improvement in SQL
timings for the CI environment status endpoint.

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/36877
上级 46bdcd64
......@@ -6,7 +6,10 @@ class Environment < ActiveRecord::Base
belongs_to :project, required: true, validate: true
has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :deployments,
-> (env) { where(project_id: env.project_id) },
dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment'
before_validation :nullify_external_url
......
---
title: Constrain environment deployments to project IDs
merge_request:
author:
type: other
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册