Skip to content

Commit 26d1ae2

Browse files
committed
Update
[ghstack-poisoned]
2 parents a181938 + d1130ec commit 26d1ae2

4 files changed

Lines changed: 226 additions & 242 deletions

File tree

aws/lambda/benchmark_regression_summary_report/common/config_model.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def to_timedelta(self) -> timedelta:
3636
else:
3737
raise ValueError(f"Unsupported unit: {self.unit}")
3838

39+
def to_timedelta_s(self) -> int:
40+
return int(self.to_timedelta().total_seconds())
41+
3942
def get_text(self):
4043
return f"{self.value}_{self.unit}"
4144

@@ -97,12 +100,23 @@ class RangeConfig:
97100
def total_timedelta(self) -> timedelta:
98101
return timedelta(days=self.baseline.value + self.comparison.value)
99102

103+
def total_timedelta_s(self) -> int:
104+
return int(
105+
timedelta(days=self.baseline.value + self.comparison.value).total_seconds()
106+
)
107+
100108
def comparison_timedelta(self) -> timedelta:
101109
return timedelta(days=self.comparison.value)
102110

111+
def comparison_timedelta_s(self) -> int:
112+
return int(self.comparison_timedelta().total_seconds())
113+
103114
def baseline_timedelta(self) -> timedelta:
104115
return timedelta(days=self.baseline.value)
105116

117+
def baseline_timedelta_s(self) -> int:
118+
return int(self.baseline_timedelta().total_seconds())
119+
106120

107121
# -------- Policy: metrics --------
108122
@dataclass
@@ -122,9 +136,7 @@ class RegressionPolicy:
122136
"greater_than", "less_than", "equal_to", "greater_equal", "less_equal"
123137
]
124138
threshold: float
125-
baseline_aggregation: Literal[
126-
"avg", "max", "min", "p50", "p90", "p95", "latest", "earliest"
127-
] = "max"
139+
baseline_aggregation: Literal["max", "min", "latest", "earliest"] = "max"
128140
rel_tol: float = 1e-3 # used only for "equal_to"
129141

130142
def is_violation(self, value: float, baseline: float) -> bool:

aws/lambda/benchmark_regression_summary_report/common/regression_utils.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22
import math
3-
import statistics
43
from typing import Any, Counter, Dict, List, Literal, Optional, Tuple, TypedDict
54

65
from common.benchmark_time_series_api_model import BenchmarkTimeSeriesApiData
@@ -15,8 +14,9 @@
1514
]
1615

1716

18-
class BaselineItem(TypedDict):
17+
class BaselineResult(TypedDict):
1918
group_info: Dict[str, Any]
19+
orignal_item: Dict[str, Any]
2020
value: float
2121

2222

@@ -27,7 +27,7 @@ class BenchmarkValueItem(TypedDict):
2727

2828
class PerGroupResult(TypedDict, total=True):
2929
group_info: Dict[str, Any]
30-
baseline: Optional[float]
30+
baseline_item: Optional[Dict[str, Any]]
3131
points: List[Any]
3232
label: RegressionClassifyLabel
3333
policy: Optional["RegressionPolicy"]
@@ -87,11 +87,11 @@ def detect_regressions_with_policies(
8787

8888
base_item = baseline_map.get(key)
8989
if not base_item:
90-
logger.warning("Skip. No baseline item found for %s", gi)
90+
logger.warning("Skip. No baseline item found for %s", key)
9191
results.append(
9292
PerGroupResult(
9393
group_info=gi,
94-
baseline=None,
94+
baseline_item=None,
9595
points=[],
9696
label="insufficient_data",
9797
policy=None,
@@ -104,44 +104,50 @@ def detect_regressions_with_policies(
104104
results.append(
105105
PerGroupResult(
106106
group_info=gi,
107-
baseline=None,
107+
baseline_item=None,
108108
points=[],
109109
label="insufficient_data",
110110
policy=None,
111111
)
112112
)
113113
continue
114-
115114
baseline_aggre_mode = policy.baseline_aggregation
116-
baseline_value = self._get_baseline(base_item, baseline_aggre_mode)
117-
if baseline_value is None or len(points) == 0:
115+
baseline_result = self._get_baseline(base_item, baseline_aggre_mode)
116+
if (
117+
not baseline_result
118+
or not baseline_result["orignal_item"]
119+
or len(points) == 0
120+
):
118121
logger.warning(
119-
"baseline_value is %s, len(points) == %s",
120-
baseline_value,
122+
"No valid baseline result found, baseline_item is %s, len(points) == %s",
123+
baseline_result,
121124
len(points),
122125
)
123126
results.append(
124127
PerGroupResult(
125128
group_info=gi,
126-
baseline=None,
129+
baseline_item=None,
127130
points=[],
128131
label="insufficient_data",
129132
policy=policy,
130133
)
131134
)
132135
continue
133136

137+
orignal_baseline_obj = baseline_result["orignal_item"]
138+
134139
# Per-point violations (True = regression)
135140
flags: List[bool] = [
136-
policy.is_violation(p["value"], baseline_value["value"]) for p in points
141+
policy.is_violation(p["value"], baseline_result["value"])
142+
for p in points
137143
]
138144
label = self.classify_flags(flags, min_points=min_points)
139145

140146
enriched_points = [{**p, "flag": f} for p, f in zip(points, flags)]
141147
results.append(
142148
PerGroupResult(
143149
group_info=gi,
144-
baseline=baseline_value["value"],
150+
baseline_item=orignal_baseline_obj,
145151
points=enriched_points,
146152
label=label,
147153
policy=policy,
@@ -202,39 +208,33 @@ def _to_data_map(
202208
def _get_baseline(
203209
self,
204210
data: BenchmarkValueItem,
205-
mode: str = "mean",
211+
mode: str = "max",
206212
field: str = "value",
207-
) -> Optional[BaselineItem]:
213+
) -> Optional[BaselineResult]:
208214
"""
209215
calculate the baseline value based on the mode
210216
mode: mean, p90, max, min, latest, p50, p95
211217
"""
212-
values = [float(d[field]) for d in data["values"] if field in d]
213-
if not values:
218+
items = [d for d in data["values"] if field in d]
219+
if not items:
214220
return None
215221

216-
if mode == "mean":
217-
val = statistics.fmean(values)
218-
elif mode == "p90":
219-
val = percentile(values, 0.9)
220-
elif mode == "max":
221-
val = max(values)
222+
if mode == "max":
223+
baseline_obj = max(items, key=lambda d: float(d[field]))
222224
elif mode == "min":
223-
val = min(values)
225+
baseline_obj = min(items, key=lambda d: float(d[field]))
224226
elif mode == "latest":
225-
val = values[-1]
227+
baseline_obj = items[-1]
226228
elif mode == "earliest":
227-
val = values[0]
228-
elif mode == "p50":
229-
val = percentile(values, 0.5)
230-
elif mode == "p95":
231-
val = percentile(values, 0.95)
229+
baseline_obj = items[0]
232230
else:
233231
logger.warning("Unknown mode: %s", mode)
234232
return None
235-
result: BaselineItem = {
233+
234+
result: BaselineResult = {
236235
"group_info": data["group_info"],
237-
"value": val,
236+
"value": float(baseline_obj[field]),
237+
"orignal_item": baseline_obj,
238238
}
239239
return result
240240

aws/lambda/benchmark_regression_summary_report/common/report_manager.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@
2727
- **Start:** `{{ time_range.start }}`
2828
- **End:** `{{ time_range.end }}`
2929
30-
## Latest Benchmark Run We Used for Report
31-
- **Timestamp:** `{{ latest.timestamp | default('') }}`
32-
- **Commit:** `{{ latest.commit | default('') }}`
33-
- **Branch:** `{{ latest.branch | default('') }}`
34-
- **Workflow ID:** `{{ latest.workflow_id | default('') }}`
3530
3631
## Summary
3732
| Metric | Value |
@@ -42,14 +37,25 @@
4237
| No Regression | {{ summary.no_regression_count | default(0) }} |
4338
| Insufficient Data | {{ summary.insufficient_data_count | default(0) }} |
4439
40+
## Latest commit
41+
- **Timestamp:** `{{ latest.timestamp | default('') }}`
42+
- **Commit:** `{{ latest.commit | default('') }}`
43+
- **Branch:** `{{ latest.branch | default('') }}`
44+
- **Workflow ID:** `{{ latest.workflow_id | default('') }}`
45+
4546
{% if regression_items and regression_items|length > 0 %}
4647
## Regression Glance
4748
4849
{% set items = regression_items if regression_items|length <= 10 else regression_items[:10] %}
4950
{% for item in items %}
5051
- **{% for k, v in item.group_info.items() %}{{ k }}={{ v }}{% if not loop.last %}, {% endif %}{% endfor %}**
51-
{% endfor %}
52-
52+
{% if item.baseline_item %}
53+
baseline commit: {{ item.baseline_item.commit }}),
54+
workflow_id: {{ item.baseline_item.workflow_id }}),
55+
timestamp:{{item.baseline_item.granularity_bucket}}
56+
{% else %}
57+
baseline item is missing
58+
{% endif %}
5359
{% if regression_items|length > 10 %}
5460
... (showing first 10 only, total {{ regression_items|length }} regressions)
5561
{% endif %}
@@ -67,7 +73,6 @@ class ReportManager:
6773
def __init__(
6874
self,
6975
db_table_name: str,
70-
config_id: str,
7176
config: BenchmarkConfig,
7277
regression_summary: Dict[str, Any],
7378
latest_meta_info: Dict[str, Any],
@@ -79,12 +84,12 @@ def __init__(
7984
):
8085
self.regression_summary = regression_summary
8186
self.regression_result = result
82-
self.config_id = config_id
87+
self.config_id = config.id
8388
self.config = config
8489
self.status = self._resolve_status(regression_summary)
8590
self.latest_meta_info = self._validate_latest_meta_info(latest_meta_info)
8691
self.report_data = self._to_report_data(
87-
config_id=config_id,
92+
config_id=config.id,
8893
summary=self.regression_summary,
8994
report=self.regression_result,
9095
latest=self.latest_meta_info,
@@ -173,9 +178,16 @@ def insert_to_db(
173178
utc_naive = aware.astimezone(dt.timezone.utc).replace(tzinfo=None)
174179
last_record_ts = utc_naive.strftime("%Y-%m-%d %H:%M:%S")
175180

176-
report_json = json.dumps(
177-
self.report_data, ensure_ascii=False, separators=(",", ":"), default=str
178-
)
181+
try:
182+
report_json = json.dumps(
183+
self.report_data, ensure_ascii=False, separators=(",", ":"), default=str
184+
)
185+
except Exception:
186+
logger.exception(
187+
"[%s] failed to serialize report data to json",
188+
self.config_id,
189+
)
190+
raise
179191

180192
params = {
181193
"id": str(self.id),
@@ -195,7 +207,6 @@ def insert_to_db(
195207
"repo": self.repo,
196208
"report_json": report_json,
197209
}
198-
199210
logger.info(
200211
"[%s]inserting benchmark regression report(%s)", self.config_id, self.id
201212
)

0 commit comments

Comments
 (0)