You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by GitBox <gi...@apache.org> on 2020/12/09 12:12:08 UTC

[GitHub] [incubator-superset] dpgaspar commented on a change in pull request #11890: fix(reports): validator_config, report state machine, working_timeout

dpgaspar commented on a change in pull request #11890:
URL: https://github.com/apache/incubator-superset/pull/11890#discussion_r539254904



##########
File path: superset/reports/commands/execute.py
##########
@@ -192,45 +200,173 @@ def _send(self, report_schedule: ReportSchedule) -> None:
         if notification_errors:
             raise ReportScheduleNotificationError(";".join(notification_errors))
 
+    def is_in_grace_period(self) -> bool:
+        """
+        Checks if an alert is on it's grace period
+        """
+        last_success = ReportScheduleDAO.find_last_success_log(
+            self._report_schedule, session=self._session
+        )
+        return (
+            last_success is not None
+            and self._report_schedule.grace_period
+            and datetime.utcnow()
+            - timedelta(seconds=self._report_schedule.grace_period)
+            < last_success.end_dttm
+        )
+
+    def is_working_timeout(self) -> bool:
+        """
+        Checks if an alert is on a working timeout
+        """
+        return (
+            self._report_schedule.working_timeout is not None
+            and self._report_schedule.last_eval_dttm is not None
+            and datetime.utcnow()
+            - timedelta(seconds=self._report_schedule.working_timeout)
+            > self._report_schedule.last_eval_dttm
+        )
+
+    def next(self) -> None:
+        raise NotImplementedError()
+
+
+class ReportNotTriggeredErrorState(BaseReportState):
+    """
+    Handle Not triggered and Error state
+    next final states:
+    - Not Triggered
+    - Success
+    - Error
+    """
+
+    current_states = [ReportState.NOOP, ReportState.ERROR]
+    initial = True
+
+    def next(self) -> None:
+        self.set_state_and_log(ReportState.WORKING)
+        try:
+            # If it's an alert check if the alert is triggered
+            if self._report_schedule.type == ReportScheduleType.ALERT:
+                if not AlertCommand(self._report_schedule).run():
+                    self.set_state_and_log(ReportState.NOOP)
+                    return
+            self._send()
+            self.set_state_and_log(ReportState.SUCCESS)
+        except CommandException as ex:
+            self.set_state_and_log(ReportState.ERROR, error_message=str(ex))
+            raise ex
+
+
+class ReportWorkingState(BaseReportState):
+    """
+    Handle Working state
+    next states:
+    - Error
+    - Working
+    """
+
+    current_states = [ReportState.WORKING]
+
+    def next(self) -> None:
+        if self.is_working_timeout():
+            exception_timeout = ReportScheduleWorkingTimeoutError()
+            self.set_state_and_log(
+                ReportState.ERROR, error_message=str(exception_timeout),
+            )
+            raise exception_timeout
+        exception_working = ReportSchedulePreviousWorkingError()
+        self.set_state_and_log(
+            ReportState.WORKING, error_message=str(exception_working),
+        )
+        raise exception_working
+
+
+class ReportSuccessState(BaseReportState):
+    """
+    Handle Success, Grace state
+    next states:
+    - Grace
+    - Not triggered
+    - Success
+    """
+
+    current_states = [ReportState.SUCCESS, ReportState.GRACE]
+
+    def next(self) -> None:
+        self.set_state_and_log(ReportState.WORKING)
+        if self._report_schedule.type == ReportScheduleType.ALERT:
+            if self.is_in_grace_period():
+                self.set_state_and_log(
+                    ReportState.GRACE,
+                    error_message=str(ReportScheduleAlertGracePeriodError()),
+                )
+                return
+            self.set_state_and_log(
+                ReportState.NOOP,
+                error_message=str(ReportScheduleAlertEndGracePeriodError()),
+            )
+            return
+        try:
+            self._send()
+            self.set_state_and_log(ReportState.SUCCESS)
+        except CommandException as ex:
+            self.set_state_and_log(ReportState.ERROR, error_message=str(ex))
+
+
+class ReportScheduleStateMachine:  # pylint: disable=too-few-public-methods
+    """
+    Simple state machine for Alerts/Reports states
+    """
+
+    states_cls = [ReportWorkingState, ReportNotTriggeredErrorState, ReportSuccessState]
+
+    def __init__(
+        self,
+        session: Session,
+        report_schedule: ReportSchedule,
+        scheduled_dttm: datetime,
+    ):
+        self._session = session
+        self._report_schedule = report_schedule
+        self._scheduled_dttm = scheduled_dttm
+
+    def run(self) -> None:

Review comment:
       good point, yet it would be very unexpected. Addressed it




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org