Browse Source

ref(stats-detectors): Expose moving averages (#60902)

This will be used to implement auto resolution. So exposing the state
here.
Tony Xiao 1 year ago
parent
commit
eee74948ad

+ 4 - 2
src/sentry/statistical_detectors/detector.py

@@ -125,7 +125,7 @@ class RegressionDetector(ABC):
     @classmethod
     def detect_trends(
         cls, projects: List[Project], start: datetime
-    ) -> Generator[Tuple[Optional[TrendType], float, DetectorPayload], None, None]:
+    ) -> Generator[Tuple[Optional[TrendType], float, DetectorPayload, DetectorState], None, None]:
         unique_project_ids: Set[int] = set()
 
         total_count = 0
@@ -153,6 +153,8 @@ class RegressionDetector(ABC):
                 algorithm = cls.detector_cls(state, cls.config)
                 trend_type, score = algorithm.update(payload)
 
+                # the trend type can be None if no update happened,
+                # pass None to indicate we do not need up update the state
                 states.append(None if trend_type is None else algorithm.state.to_redis_dict())
 
                 if trend_type == TrendType.Regressed:
@@ -162,7 +164,7 @@ class RegressionDetector(ABC):
 
                 unique_project_ids.add(payload.project_id)
 
-                yield (trend_type, score, payload)
+                yield (trend_type, score, payload, algorithm.state)
 
             cls.store.bulk_write_states(payloads, states)
 

+ 10 - 3
src/sentry/tasks/statistical_detectors.py

@@ -51,7 +51,12 @@ from sentry.statistical_detectors.algorithm import (
     MovingAverageRelativeChangeDetector,
     MovingAverageRelativeChangeDetectorConfig,
 )
-from sentry.statistical_detectors.detector import DetectorPayload, RegressionDetector, TrendType
+from sentry.statistical_detectors.detector import (
+    DetectorPayload,
+    DetectorState,
+    RegressionDetector,
+    TrendType,
+)
 from sentry.statistical_detectors.issue_platform_adapter import (
     fingerprint_regression,
     send_regression_to_platform,
@@ -1038,14 +1043,16 @@ def query_functions_timeseries(
 
 
 def limit_regressions_by_project(
-    trends: Generator[Tuple[Optional[TrendType], float, DetectorPayload], None, None],
+    trends: Generator[
+        Tuple[Optional[TrendType], float, DetectorPayload, DetectorState], None, None
+    ],
     ratelimit: int,
 ) -> Generator[DetectorPayload, None, None]:
     regressions_by_project: DefaultDict[int, List[Tuple[float, DetectorPayload]]] = defaultdict(
         list
     )
 
-    for trend_type, score, payload in trends:
+    for trend_type, score, payload, state in trends:
         if trend_type != TrendType.Regressed:
             continue
         heapq.heappush(regressions_by_project[payload.project_id], (score, payload))

+ 7 - 6
tests/sentry/tasks/test_statistical_detectors.py

@@ -406,12 +406,13 @@ def test_limit_regressions_by_project(ratelimit, timestamp, expected_idx):
     }
 
     def trends():
-        yield (None, 0, payloads[(1, 1)])
-        yield (TrendType.Improved, 0, payloads[(2, 1)])
-        yield (TrendType.Regressed, 0, payloads[(2, 2)])
-        yield (TrendType.Regressed, 0, payloads[(3, 1)])
-        yield (TrendType.Regressed, 1, payloads[(3, 2)])
-        yield (TrendType.Regressed, 2, payloads[(3, 3)])
+        # we do not need the detector state here so mock it with None
+        yield (None, 0, payloads[(1, 1)], None)
+        yield (TrendType.Improved, 0, payloads[(2, 1)], None)
+        yield (TrendType.Regressed, 0, payloads[(2, 2)], None)
+        yield (TrendType.Regressed, 0, payloads[(3, 1)], None)
+        yield (TrendType.Regressed, 1, payloads[(3, 2)], None)
+        yield (TrendType.Regressed, 2, payloads[(3, 3)], None)
 
     expected_regressions = [
         payloads[(2, 2)],