Skip code navigation lookups when a project has no LSIF artifacts
What does this MR do and why?
Gitlab::CodeNavigationPath#build runs on the blob page and on every
merge request diff render. Today it always shells out to Gitaly for the
latest commits and then queries every pipeline of those shas — even for
projects that never produced an LSIF artifact and can therefore never
render code navigation.
This MR adds an early guard: when the project has no LSIF artifacts,
skip the Gitaly commit lookup and the pipeline lookup entirely and
return nil. The existence check is an index-only LIMIT 1 query, far
cheaper than the lookups it avoids, and the result is memoized for the
rest of the request.
Split out of !263784.
Database
LSIF artifact existence check:
SELECT 1 AS one FROM "p_ci_job_artifacts"
WHERE "p_ci_job_artifacts"."project_id" = ? AND "p_ci_job_artifacts"."file_type" = 15
LIMIT 1Plan
execution plan (EXPLAIN ANALYZE):
Limit (cost=0.70..4.06 rows=1 width=4) (actual time=162.598..162.607 rows=0 loops=1)
Buffers: shared read=65
I/O Timings: read=161.188 write=0.000
-> Append (cost=0.70..57.70 rows=17 width=4) (actual time=162.595..162.603 rows=0 loops=1)
Buffers: shared read=65
I/O Timings: read=161.188 write=0.000
-> Index Only Scan using index_ci_job_artifacts_on_id_project_id_and_file_type on gitlab_partitions_dynamic.ci_job_artifacts p_ci_job_artifacts_1 (cost=0.70..3.72 rows=1 width=4) (actual time=17.080..17.081 rows=0 loops=1)
Index Cond: ((p_ci_job_artifacts_1.project_id = ?) AND (p_ci_job_artifacts_1.file_type = 15))
Heap Fetches: 0
Buffers: shared read=5
I/O Timings: read=16.965 write=0.000
-> Index Only Scan using ci_job_artifacts_102_project_id_file_type_id_idx on gitlab_partitions_dynamic.ci_job_artifacts_102 p_ci_job_artifacts_2 (cost=0.58..3.60 rows=1 width=4) (actual time=20.238..20.238 rows=0 loops=1)
Index Cond: ((p_ci_job_artifacts_2.project_id = ?) AND (p_ci_job_artifacts_2.file_type = 15))
Heap Fetches: 0
Buffers: shared read=4
I/O Timings: read=20.148 write=0.000
-> Index Only Scan using ci_job_artifacts_103_project_id_file_type_id_idx on gitlab_partitions_dynamic.ci_job_artifacts_103 p_ci_job_artifacts_3 (cost=0.57..3.59 rows=1 width=4) (actual time=10.843..10.843 rows=0 loops=1)
Index Cond: ((p_ci_job_artifacts_3.project_id = ?) AND (p_ci_job_artifacts_3.file_type = 15))
Heap Fetches: 0
Buffers: shared read=4
I/O Timings: read=10.753 write=0.000
-> Index Only Scan using ci_job_artifacts_104_project_id_file_type_id_idx on gitlab_partitions_dynamic.ci_job_artifacts_104 p_ci_job_artifacts_4 (cost=0.57..3.59 rows=1 width=4) (actual time=15.301..15.301 rows=0 loops=1)
Index Cond: ((p_ci_job_artifacts_4.project_id = ?) AND (p_ci_job_artifacts_4.file_type = 15))
Heap Fetches: 0
Buffers: shared read=4
I/O Timings: read=15.198 write=0.000
-> Index Only Scan using ci_job_artifacts_105_project_id_file_type_id_idx on gitlab_partitions_dynamic.ci_job_artifacts_105 p_ci_job_artifacts_5 (cost=0.57..3.59 rows=1 width=4) (actual time=11.853..11.854 rows=0 loops=1)
Index Cond: ((p_ci_job_artifacts_5.project_id = ?) AND (p_ci_job_artifacts_5.file_type = 15))
Heap Fetches: 0
Buffers: shared read=4
I/O Timings: read=11.772 write=0.000
-> Seq Scan on gitlab_partitions_dynamic.ci_job_artifacts_117 p_ci_job_artifacts_17 (cost=0.00..0.00 rows=1 width=4) (actual time=0.025..0.025 rows=0 loops=1)
Filter: ((p_ci_job_artifacts_17.project_id = ?) AND (p_ci_job_artifacts_17.file_type = 15))
I/O Timings: read=0.000 write=0.000
stats:
Time: 204.444 ms
- planning: 41.623 ms
- execution: 162.821 ms
- I/O read: 161.188 ms
- I/O write: 0.000 ms
Shared buffers:
- hits: 0 from the buffer pool
- reads: 65 (~520.00 KiB) from the OS file cache, including disk I/O
- dirtied: 0
- writes: 0References
- !263784 (parent MR this is split out of)
Screenshots or screen recordings
No UI change. When a project has no LSIF artifacts, code navigation is
absent exactly as before; the lookups that would have produced that same
nil are simply skipped.
How to set up and validate locally
-
In a Rails console, pick a project with no LSIF artifacts and confirm the lookups are skipped:
project = Project.find_by_full_path('<some-project>') project.job_artifacts.with_file_types(['lsif']).exists? # => false # Spy on the Gitaly lookup that should no longer happen: allow = -> { raise 'Gitaly lookup was not skipped' } project.repository.define_singleton_method(:commits) { |*| allow.call } Gitlab::CodeNavigationPath.new(project, project.commit.sha) .full_json_path_for('README.md') # => nil, without Gitaly or a pipeline query -
For a project that does have an LSIF artifact, code navigation links still render on blob pages as before.
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.