Просмотр исходного кода

fix(escalating): Return forecast for one event if no forecast exists (#53088)

Return the forecast for one event if no forecast exists in nodestore
Jodi Jang 1 год назад
Родитель
Сommit
033041c55d

+ 12 - 1
src/sentry/issues/escalating_group_forecast.py

@@ -15,6 +15,7 @@ from sentry import nodestore
 from sentry.utils.dates import parse_timestamp
 
 GROUP_FORECAST_TTL = 14
+ONE_EVENT_FORECAST = [10] * 14
 
 
 class EscalatingGroupForecastData(TypedDict):
@@ -48,6 +49,11 @@ class EscalatingGroupForecast:
 
     @classmethod
     def fetch(cls, project_id: int, group_id: int) -> Optional[EscalatingGroupForecast]:
+        """
+        Return the forecast from nodestore if it exists.
+        If it does not exist, it is because the TTL expired and the issue has not been seen in 7
+        days. Generate the forecast in a task, and return the forecast for one event.
+        """
         from sentry.issues.forecasts import generate_and_save_missing_forecasts
 
         results = nodestore.get(cls.build_storage_identifier(project_id, group_id))
@@ -57,7 +63,12 @@ class EscalatingGroupForecast:
             f"Forecast does not exist for project id: {str(project_id)} group id: {str(group_id)}"
         )
         generate_and_save_missing_forecasts.delay(group_id=group_id)
-        return None
+        return EscalatingGroupForecast(
+            project_id=project_id,
+            group_id=group_id,
+            forecast=ONE_EVENT_FORECAST,
+            date_added=datetime.now(),
+        )
 
     @classmethod
     def fetch_todays_forecast(cls, project_id: int, group_id: int) -> Optional[int]:

+ 3 - 2
src/sentry/issues/forecasts.py

@@ -68,8 +68,9 @@ def generate_and_save_forecasts(groups: Sequence[Group]) -> None:
 )
 def generate_and_save_missing_forecasts(group_id: int) -> None:
     """
-    Runs generate_and_save_forecasts in a task as a fallback if the forecast does not exist.
-    This should not happen, but exists as a fallback.
+    Runs generate_and_save_forecasts in a task if the forecast does not exist.
+    This will happen if the forecast in nodestore TTL expired and the issue has not been seen in
+    7 days.
     """
     group = Group.objects.filter(id=group_id)
     generate_and_save_forecasts(group)

+ 2 - 2
tests/sentry/issues/test_ignored.py

@@ -1,6 +1,6 @@
 from unittest.mock import MagicMock, patch
 
-from sentry.issues.escalating_group_forecast import EscalatingGroupForecast
+from sentry.issues.escalating_group_forecast import ONE_EVENT_FORECAST, EscalatingGroupForecast
 from sentry.issues.ignored import handle_archived_until_escalating, handle_ignored
 from sentry.models import (
     Group,
@@ -86,7 +86,7 @@ class HandleArchiveUntilEscalating(TestCase):
         assert status_details == {"ignoreUntilEscalating": True}
 
         fetched_forecast = EscalatingGroupForecast.fetch(self.group.project.id, self.group.id)
-        assert fetched_forecast is None
+        assert fetched_forecast and fetched_forecast.forecast == ONE_EVENT_FORECAST
         assert mock_logger.exception.call_args.args[0] == (
             f"Forecast does not exist for project id: {self.group.project.id} group id: {str(self.group.id)}"
         )

+ 6 - 2
tests/sentry/tasks/test_weekly_escalating_forecast.py

@@ -4,7 +4,7 @@ from unittest.mock import MagicMock, patch
 
 import pytz
 
-from sentry.issues.escalating_group_forecast import EscalatingGroupForecast
+from sentry.issues.escalating_group_forecast import ONE_EVENT_FORECAST, EscalatingGroupForecast
 from sentry.models.group import Group, GroupStatus
 from sentry.tasks.weekly_escalating_forecast import run_escalating_forecast
 from sentry.testutils.cases import APITestCase, SnubaTestCase
@@ -33,6 +33,10 @@ class TestWeeklyEscalatingForecast(APITestCase, SnubaTestCase):
         mock_logger: MagicMock,
         mock_generate_and_save_missing_forecasts: MagicMock,
     ) -> None:
+        """
+        Test that when fetch is called and the issue has no forecast, the forecast for one
+        event/hr is returned, and the forecast is regenerated.
+        """
         with self.tasks():
             group_list = self.create_archived_until_escalating_groups(num_groups=1)
 
@@ -41,7 +45,7 @@ class TestWeeklyEscalatingForecast(APITestCase, SnubaTestCase):
             run_escalating_forecast()
             group = group_list[0]
             fetched_forecast = EscalatingGroupForecast.fetch(group.project.id, group.id)
-            assert fetched_forecast is None
+            assert fetched_forecast and fetched_forecast.forecast == ONE_EVENT_FORECAST
             assert mock_logger.exception.call_args.args[0] == (
                 f"Forecast does not exist for project id: {group.project.id} group id: {group.id}"
             )