Skip to content

Commit a5fecbf

Browse files
feat: split the "full" tree details data fetch into three separate queries (#1770)
* feat: enhance tree details functionality with new splitted retrieval methods * refactor: update tree details lazy loading to separate builds, boots and tests queries
1 parent 0607fde commit a5fecbf

10 files changed

Lines changed: 275 additions & 66 deletions

File tree

backend/kernelCI_app/constants/localization.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ class ClientStrings:
1010
"""Simple class for storing basis for internationalization strings"""
1111

1212
TREE_BUILDS_NO_RESULTS = "No builds available for this tree/branch/commit"
13-
TREE_BOOTS_NO_RESULTS = "No boots available for this tree/branch/commit"
1413
TREE_NO_RESULTS = "No results available for this tree/branch/commit"
15-
TREE_TESTS_NO_RESULTS = "No tests available for this tree/branch/commit"
1614
TREE_COMMITS_HISTORY_NOT_FOUND = "History of tree commits not found"
1715
TREE_NOT_FOUND_IN_INTERVAL = "Tree not found in the given interval"
1816
TREE_REPORT_MIN_MAX_AGE = (

backend/kernelCI_app/queries/tree.py

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional
1+
from typing import Literal, Optional
22
from django.db import connection
33
from django.db.models import Q
44

@@ -465,6 +465,174 @@ def get_tree_details_data(
465465
return rows
466466

467467

468+
def get_tree_data(
469+
*,
470+
data_type: Literal["builds", "boots", "tests"],
471+
origin_param: str,
472+
git_url_param: Optional[str],
473+
git_branch_param: Optional[str],
474+
commit_hash: Optional[str],
475+
tree_name: Optional[str] = None,
476+
) -> Optional[list[tuple]]:
477+
"""Fetch build, boot, or test rows for a given tree commit."""
478+
cache_key = f"treeDetails{data_type.capitalize()}"
479+
480+
params = {
481+
"commit_hash": commit_hash,
482+
"tree_name": tree_name,
483+
"origin_param": origin_param,
484+
"git_url_param": git_url_param,
485+
"git_branch_param": git_branch_param,
486+
}
487+
488+
rows = get_query_cache(cache_key, params)
489+
if rows is None:
490+
checkout_clauses = create_checkouts_where_clauses(
491+
git_url=git_url_param,
492+
git_branch=git_branch_param,
493+
tree_name=tree_name,
494+
)
495+
496+
git_branch_clause = checkout_clauses.get("git_branch_clause")
497+
tree_name_clause = checkout_clauses.get("tree_name_clause")
498+
git_url_clause = checkout_clauses.get("git_url_clause")
499+
tree_name_full_clause = "AND " + tree_name_clause if tree_name_clause else ""
500+
git_url_full_clause = "AND " + git_url_clause if git_url_clause else ""
501+
502+
is_boots = data_type == "boots"
503+
is_tests = data_type == "tests"
504+
include_test_cols = is_boots or is_tests
505+
506+
tests_select = (
507+
"""
508+
tests.id AS tests_id,
509+
tests.origin,
510+
tests.environment_comment AS tests_environment_comment,
511+
tests.environment_misc AS tests_environment_misc,
512+
tests.path AS tests_path,
513+
tests.comment AS tests_comment,
514+
tests.log_url AS tests_log_url,
515+
tests.status AS tests_status,
516+
tests.start_time AS tests_start_time,
517+
tests.duration AS tests_duration,
518+
tests.number_value AS tests_number_value,
519+
tests.misc AS tests_misc,
520+
tests.environment_compatible AS tests_environment_compatible,"""
521+
if include_test_cols
522+
else """
523+
NULL AS tests_id,
524+
NULL AS tests_origin,
525+
NULL AS tests_environment_comment,
526+
NULL AS tests_environment_misc,
527+
NULL AS tests_path,
528+
NULL AS tests_comment,
529+
NULL AS tests_log_url,
530+
NULL AS tests_status,
531+
NULL AS tests_start_time,
532+
NULL AS tests_duration,
533+
NULL AS tests_number_value,
534+
NULL AS tests_misc,
535+
NULL AS tests_environment_compatible,"""
536+
)
537+
538+
tests_join = ""
539+
if is_boots:
540+
tests_join = (
541+
"LEFT JOIN tests ON builds_filter.builds_id = tests.build_id"
542+
" AND (tests.path = 'boot' OR tests.path LIKE 'boot.%%')"
543+
)
544+
elif is_tests:
545+
tests_join = (
546+
"LEFT JOIN tests ON builds_filter.builds_id = tests.build_id"
547+
" AND tests.path <> 'boot' AND tests.path NOT LIKE 'boot.%%'"
548+
)
549+
550+
incidents_on = (
551+
"tests.id = incidents.test_id"
552+
if include_test_cols
553+
else "builds_filter.builds_id = incidents.build_id"
554+
)
555+
556+
query = f"""
557+
WITH RELEVANT_HASH AS (
558+
SELECT
559+
c.git_commit_hash
560+
FROM
561+
checkouts c
562+
WHERE
563+
c.git_commit_hash = %(commit_hash)s
564+
OR %(commit_hash)s = ANY (c.git_commit_tags)
565+
ORDER BY
566+
c._timestamp DESC
567+
LIMIT 1
568+
)
569+
SELECT
570+
{tests_select}
571+
builds_filter.*,
572+
incidents.id AS incidents_id,
573+
incidents.test_id AS incidents_test_id,
574+
incidents.present AS incidents_present,
575+
issues.id AS issues_id,
576+
issues.version AS issues_version,
577+
issues.comment AS issues_comment,
578+
issues.report_url AS issues_report_url
579+
FROM
580+
(
581+
SELECT
582+
builds.id AS builds_id,
583+
builds.origin,
584+
builds.comment AS builds_comment,
585+
builds.start_time AS builds_start_time,
586+
builds.duration AS builds_duration,
587+
builds.architecture AS builds_architecture,
588+
builds.command AS builds_command,
589+
builds.compiler AS builds_compiler,
590+
builds.config_name AS builds_config_name,
591+
builds.config_url AS builds_config_url,
592+
builds.log_url AS builds_log_url,
593+
builds.status AS builds_valid,
594+
builds.misc AS builds_misc,
595+
tree_head.*
596+
FROM
597+
(
598+
SELECT
599+
checkouts.id AS checkout_id,
600+
checkouts.git_repository_url AS checkouts_git_repository_url,
601+
checkouts.git_repository_branch AS checkouts_git_repository_branch,
602+
checkouts.git_commit_tags AS checkout_git_commit_tags,
603+
checkouts.origin AS checkouts_origin
604+
FROM
605+
checkouts
606+
WHERE
607+
checkouts.git_commit_hash = (
608+
SELECT git_commit_hash FROM RELEVANT_HASH
609+
)
610+
{git_url_full_clause}
611+
{tree_name_full_clause}
612+
AND {git_branch_clause}
613+
AND checkouts.origin = %(origin_param)s
614+
) AS tree_head
615+
LEFT JOIN builds
616+
ON tree_head.checkout_id = builds.checkout_id
617+
) AS builds_filter
618+
{tests_join}
619+
LEFT JOIN incidents
620+
ON {incidents_on}
621+
LEFT JOIN issues
622+
ON incidents.issue_id = issues.id
623+
AND incidents.issue_version = issues.version
624+
ORDER BY
625+
issues."_timestamp" DESC
626+
"""
627+
628+
with connection.cursor() as cursor:
629+
cursor.execute(query, params)
630+
rows = cursor.fetchall()
631+
set_query_cache(key=cache_key, params=params, rows=rows)
632+
633+
return rows
634+
635+
468636
GIT_BRANCH_FIELD = "git_repository_branch"
469637
GIT_URL_FIELD = "git_repository_url"
470638

backend/kernelCI_app/views/treeDetailsBootsView.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
decide_if_is_full_row_filtered_out,
1717
get_current_row_data,
1818
)
19-
from kernelCI_app.queries.tree import get_tree_details_data
19+
from kernelCI_app.queries.tree import get_tree_data
2020
from kernelCI_app.typeModels.commonOpenApiParameters import (
2121
COMMIT_HASH_PATH_PARAM,
2222
GIT_BRANCH_PATH_PARAM,
@@ -76,7 +76,8 @@ def get(
7676
commit_hash: str,
7777
origin: str,
7878
) -> Response:
79-
rows = get_tree_details_data(
79+
rows = get_tree_data(
80+
data_type="boots",
8081
origin_param=origin,
8182
git_url_param=git_url,
8283
git_branch_param=git_branch,
@@ -92,14 +93,6 @@ def get(
9293
status_code=HTTPStatus.OK,
9394
)
9495

95-
if len(rows) == 1:
96-
row_data = get_current_row_data(current_row=rows[0])
97-
if row_data["test_id"] is None:
98-
return create_api_error_response(
99-
error_message=ClientStrings.TREE_BOOTS_NO_RESULTS,
100-
status_code=HTTPStatus.OK,
101-
)
102-
10396
try:
10497
self._sanitize_rows(rows)
10598

backend/kernelCI_app/views/treeDetailsBuildsView.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
get_build,
1919
get_current_row_data,
2020
)
21-
from kernelCI_app.queries.tree import get_tree_details_data
21+
from kernelCI_app.queries.tree import get_tree_data
2222
from kernelCI_app.typeModels.commonOpenApiParameters import (
2323
COMMIT_HASH_PATH_PARAM,
2424
GIT_BRANCH_PATH_PARAM,
@@ -78,7 +78,8 @@ def get(
7878
commit_hash: str,
7979
origin: str,
8080
) -> Response:
81-
rows = get_tree_details_data(
81+
rows = get_tree_data(
82+
data_type="builds",
8283
origin_param=origin,
8384
git_url_param=git_url,
8485
git_branch_param=git_branch,

backend/kernelCI_app/views/treeDetailsTestsView.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
decide_if_is_test_filtered_out,
1616
get_current_row_data,
1717
)
18-
from kernelCI_app.queries.tree import get_tree_details_data
18+
from kernelCI_app.queries.tree import get_tree_data
1919
from kernelCI_app.typeModels.commonOpenApiParameters import (
2020
COMMIT_HASH_PATH_PARAM,
2121
GIT_BRANCH_PATH_PARAM,
@@ -81,7 +81,8 @@ def get(
8181
commit_hash: str,
8282
origin: str,
8383
) -> Response:
84-
rows = get_tree_details_data(
84+
rows = get_tree_data(
85+
data_type="tests",
8586
origin_param=origin,
8687
git_url_param=git_url,
8788
git_branch_param=git_branch,
@@ -97,14 +98,6 @@ def get(
9798
status_code=HTTPStatus.OK,
9899
)
99100

100-
if len(rows) == 1:
101-
row_data = get_current_row_data(current_row=rows[0])
102-
if row_data["test_id"] is None:
103-
return create_api_error_response(
104-
error_message=ClientStrings.TREE_TESTS_NO_RESULTS,
105-
status_code=HTTPStatus.OK,
106-
)
107-
108101
try:
109102
self._sanitize_rows(rows)
110103

0 commit comments

Comments
 (0)