Fix n+1 query issue on dependencies controllers

What does this MR do and why?

The Dependency List UI uses has_dependency_paths returned from /dependencies (or /dependencies/location in case of Group Dependency List) to decide whether or not to display View Dependency Paths. This value was previously calculated by making n calls to the DB for n occurrences. This MR fixes that to make only 1 query.

  1. Moves has_dependency_paths from LocationEntity to be directly under DependencyEntity
    • Makes more sense that dependency has dependency paths
    • Also fixes n+1 query issues on Sbom::Exporters::CsvService and Sbom::Exporters::JsonArrayService, since they don't use the has_dependency_paths attribute at all.
    • Only the dependencies controllers use the has_dependency_paths attr.
  2. Updates UI to match the above change.
  3. Updates specs.

DB changes -

Project Dependency list query -

Before
SELECT "sbom_occurrences".*
FROM "sbom_occurrences"
LEFT OUTER JOIN "sbom_sources" ON "sbom_sources"."id" = "sbom_occurrences"."source_id"
WHERE "sbom_occurrences"."project_id" = 1
  AND "sbom_occurrences"."traversal_ids" = '{1}'
  AND "sbom_occurrences"."archived" = FALSE
  AND "sbom_occurrences"."project_id" = 1
  AND ("sbom_sources"."source_type" IN (0,
                                        1)
       OR "sbom_sources"."source_type" IS NULL)
ORDER BY "sbom_occurrences"."highest_severity" DESC NULLS LAST
LIMIT 20
OFFSET 0

plan - https://console.postgres.ai/gitlab/gitlab-production-sec/sessions/41737/commands/128096

After
SELECT "sbom_occurrences".*,
       EXISTS (
                 (SELECT 1
                  FROM "sbom_graph_paths"
                  WHERE "sbom_graph_paths"."descendant_id" = "sbom_occurrences"."id"
                  LIMIT 1)) AS has_dependency_paths
FROM "sbom_occurrences"
LEFT OUTER JOIN "sbom_sources" ON "sbom_sources"."id" = "sbom_occurrences"."source_id"
WHERE "sbom_occurrences"."project_id" = 1
  AND "sbom_occurrences"."traversal_ids" = '{1}'
  AND "sbom_occurrences"."archived" = FALSE
  AND "sbom_occurrences"."project_id" = 1
  AND ("sbom_sources"."source_type" IN (0,
                                        1)
       OR "sbom_sources"."source_type" IS NULL)
ORDER BY "sbom_occurrences"."highest_severity" DESC NULLS LAST
LIMIT 20
OFFSET 0;

plan - https://console.postgres.ai/gitlab/gitlab-production-sec/sessions/41737/commands/128095

/dependencies/locations call on Group Dependency list page -

Before
SELECT "sbom_occurrences".*
FROM "sbom_occurrences"
WHERE (sbom_occurrences.traversal_ids >= '{1}'
       AND '{2}' > sbom_occurrences.traversal_ids)
  AND "sbom_occurrences"."component_version_id" = 1
LIMIT 50;

plan - https://console.postgres.ai/gitlab/gitlab-production-sec/sessions/41737/commands/128099

After
SELECT "sbom_occurrences".*,
       EXISTS (
                 (SELECT 1
                  FROM "sbom_graph_paths"
                  WHERE "sbom_graph_paths"."descendant_id" = "sbom_occurrences"."id"
                  LIMIT 1)) AS has_dependency_paths
FROM "sbom_occurrences"
WHERE (sbom_occurrences.traversal_ids >= '{1}'
       AND '{2}' > sbom_occurrences.traversal_ids)
  AND "sbom_occurrences"."component_version_id" = 1
LIMIT 50;

plan - https://console.postgres.ai/gitlab/gitlab-production-sec/sessions/41737/commands/128100

References

#530213 (closed)

Screenshots or screen recordings

Before After

How to set up and validate locally

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Rushik Subba

Merge request reports

Loading