Skip to content

Commit 32989ef

Browse files
mtopo27claude
andcommitted
feat(preprod): Persist comparison records for sequential monitor evaluations
Wire _run_size_analysis_comparison into the sequential (n-1) monitor path so that PreprodArtifactSizeComparison records are created and file-level diffs are persisted, matching the git-based status check path. Base metrics are now fetched once per unique base artifact and cached, removing redundant per-detector DB queries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 59a48d9 commit 32989ef

File tree

2 files changed

+55
-10
lines changed

2 files changed

+55
-10
lines changed

src/sentry/preprod/size_analysis/tasks.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -632,16 +632,13 @@ def _maybe_emit_issues_from_diff_size_results(
632632
base = None
633633
query_to_base[normalized_query] = base
634634

635-
for detector in detectors:
636-
query = detector.config.get("query", "")
637-
normalized_query = query.strip() if query else ""
638-
base_artifact = query_to_base.get(normalized_query)
639-
if base_artifact is None:
640-
logger.info(
641-
"preprod.size_analysis.diff_results.no_base",
642-
extra={"detector_id": detector.id, "artifact_id": artifact.id},
643-
)
635+
# Run comparisons for unique sequential bases (persists file-level diffs)
636+
base_artifact_to_metric: dict[int, PreprodArtifactSizeMetrics] = {}
637+
seen_base_ids: set[int] = set()
638+
for base_artifact in query_to_base.values():
639+
if base_artifact is None or base_artifact.id in seen_base_ids:
644640
continue
641+
seen_base_ids.add(base_artifact.id)
645642

646643
base_metrics = list(
647644
PreprodArtifactSizeMetrics.objects.filter(
@@ -654,6 +651,31 @@ def _maybe_emit_issues_from_diff_size_results(
654651
continue
655652

656653
base_metric = base_metrics[0]
654+
base_artifact_to_metric[base_artifact.id] = base_metric
655+
656+
with transaction.atomic(router.db_for_write(PreprodArtifactSizeComparison)):
657+
PreprodArtifactSizeComparison.objects.get_or_create(
658+
head_size_analysis=head_metric,
659+
base_size_analysis=base_metric,
660+
organization_id=org_id,
661+
defaults={"state": PreprodArtifactSizeComparison.State.PENDING},
662+
)
663+
_run_size_analysis_comparison(org_id, head_metric, base_metric)
664+
665+
for detector in detectors:
666+
query = detector.config.get("query", "")
667+
normalized_query = query.strip() if query else ""
668+
base_artifact = query_to_base.get(normalized_query)
669+
if base_artifact is None:
670+
logger.info(
671+
"preprod.size_analysis.diff_results.no_base",
672+
extra={"detector_id": detector.id, "artifact_id": artifact.id},
673+
)
674+
continue
675+
676+
base_metric = base_artifact_to_metric.get(base_artifact.id)
677+
if base_metric is None:
678+
continue
657679

658680
metadata: SizeAnalysisMetadata = {
659681
"platform": _get_platform(artifact),

tests/sentry/preprod/size_analysis/test_size_analysis_tasks.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
from django.utils import timezone
55

6-
from sentry.preprod.models import PreprodArtifact, PreprodArtifactSizeMetrics
6+
from sentry.preprod.models import (
7+
PreprodArtifact,
8+
PreprodArtifactSizeComparison,
9+
PreprodArtifactSizeMetrics,
10+
)
711
from sentry.preprod.size_analysis.grouptype import (
812
PreprodSizeAnalysisGroupType,
913
_artifact_to_tags,
@@ -220,6 +224,25 @@ def test_batches_queries(self):
220224
# Should only be called once for the shared empty query
221225
assert mock_lookup.call_count == 1
222226

227+
def test_creates_comparison_record(self):
228+
now = timezone.now()
229+
self._create_artifact_with_metrics(
230+
max_install_size=4000000,
231+
date_added=now - timedelta(hours=2),
232+
)
233+
head = self._create_artifact_with_metrics(
234+
max_install_size=5000000,
235+
date_added=now - timedelta(hours=1),
236+
)
237+
self._create_diff_detector()
238+
239+
with self.feature("organizations:preprod-issues"):
240+
maybe_emit_issues_from_diff_size_results(head, self.organization.id)
241+
242+
assert PreprodArtifactSizeComparison.objects.count() == 1
243+
comparison = PreprodArtifactSizeComparison.objects.first()
244+
assert comparison is not None
245+
223246

224247
class MaybeEmitIssuesFromSizeResultsTest(TestCase):
225248
"""Tests for the maybe_emit_issues_from_absolute_size_results function."""

0 commit comments

Comments
 (0)