From e9d60394bfb4e3f65d36cddf796b1394d29503a4 Mon Sep 17 00:00:00 2001 From: Gregory Mierzwinski Date: Thu, 23 Apr 2026 21:21:40 -0400 Subject: [PATCH] Bug 2034671 - Ingest suite-level replicates from performance data. --- tests/etl/test_perf_data_load.py | 19 +++++++++++++++++++ treeherder/etl/perf.py | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/tests/etl/test_perf_data_load.py b/tests/etl/test_perf_data_load.py index 11259cde567..9e7d3e61b24 100644 --- a/tests/etl/test_perf_data_load.py +++ b/tests/etl/test_perf_data_load.py @@ -16,6 +16,7 @@ from treeherder.perf.models import ( MultiCommitDatum, PerformanceDatum, + PerformanceDatumReplicate, PerformanceFramework, PerformanceSignature, ) @@ -523,3 +524,21 @@ def test_multi_commit_data_is_removed_by_dedicated_management_script( assert ( PerformanceDatum.objects.all().count() == DATA_PER_ARTIFACT ) # data ingested in the old way remains intact + + +def test_suite_replicates_are_ingested(test_repository, perf_job, sample_perf_artifact): + suite_replicates = [1.0, 2.0, 3.0] + sample_perf_artifact["blob"]["suites"][0]["replicates"] = suite_replicates + _, submit_datum = _prepare_test_data(sample_perf_artifact) + + store_performance_artifact(perf_job, submit_datum) + + suite_datum = PerformanceDatum.objects.get( + signature=PerformanceSignature.objects.get(suite="youtube-watch", test="") + ) + stored_replicates = list( + PerformanceDatumReplicate.objects.filter(performance_datum=suite_datum).values_list( + "value", flat=True + ) + ) + assert sorted(stored_replicates) == sorted(suite_replicates) diff --git a/treeherder/etl/perf.py b/treeherder/etl/perf.py index a1d30ff35e9..bf7c88dd52f 100644 --- a/treeherder/etl/perf.py +++ b/treeherder/etl/perf.py @@ -227,6 +227,23 @@ def _load_perf_datum(job: Job, perf_datum: dict): # keep a register with all multi commit perf data MultiCommitDatum.objects.create(perf_datum=suite_datum) + replicates = suite.get("replicates", []) + if replicates and len(replicates) > 0: + try: + # Add the replicates to the PerformanceDatumReplicate table, and + # catch and ignore any exceptions that are produced here so we don't + # impact the standard workflow + PerformanceDatumReplicate.objects.bulk_create( + [ + PerformanceDatumReplicate( + value=replicate, performance_datum=suite_datum + ) + for replicate in replicates + ] + ) + except Exception as e: + logger.info(f"Failed to ingest replicates for suite datum {suite_datum}: {e}") + if _suite_should_alert_based_on(signature, job, datum_created): generate_alerts.apply_async(args=[signature.id], queue="generate_perf_alerts")