-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ feat(aci): Invoke Rule Registry from Notification Action #84524
Conversation
"sentry.workflow_engine.handlers.action.notification.issue_alert.instantiate_action" | ||
) | ||
@mock.patch("uuid.uuid4") | ||
def test_invoke_legacy_registry(self, mock_uuid, mock_instantiate_action, mock_safe_execute): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
based off of
sentry/src/sentry/rules/processing/processor.py
Lines 147 to 184 in aa95456
def activate_downstream_actions( | |
rule: Rule, | |
event: GroupEvent, | |
notification_uuid: str | None = None, | |
rule_fire_history: RuleFireHistory | None = None, | |
) -> MutableMapping[ | |
str, tuple[Callable[[GroupEvent, Sequence[RuleFuture]], None], list[RuleFuture]] | |
]: | |
grouped_futures: MutableMapping[ | |
str, tuple[Callable[[GroupEvent, Sequence[RuleFuture]], None], list[RuleFuture]] | |
] = {} | |
for action in rule.data.get("actions", ()): | |
action_inst = instantiate_action(rule, action, rule_fire_history) | |
if not action_inst: | |
continue | |
results = safe_execute( | |
action_inst.after, | |
event=event, | |
notification_uuid=notification_uuid, | |
) | |
if results is None: | |
logger.warning("Action %s did not return any futures", action["id"]) | |
continue | |
for future in results: | |
key = future.key if future.key is not None else future.callback | |
rule_future = RuleFuture(rule=rule, kwargs=future.kwargs) | |
if key not in grouped_futures: | |
grouped_futures[key] = (future.callback, [rule_future]) | |
else: | |
grouped_futures[key][1].append(rule_future) | |
return grouped_futures | |
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
detector: Detector, | ||
) -> None: | ||
""" | ||
This method will create a rule instance from the Action model, and then invoke the legacy registry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which part of this is doing the invocation? it looks like it's just executing the futures like we do currently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i added a lot more docstrings just now. the tldr is we consolidate the post_process process_rules
, activate_downstream_actions
into this method which will handle building a rule instance from an Action, getting the action handler class, getting the rule futures and executing them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not seeing the use of any registry here, maybe that's what colleen was asking about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes, good point. the rule registry is quite literally invoked in the activate_downstream_actions
with the instantiate_action
method call it makes. do you think there is a better way for me to be explicit about this?
6394eb6
to
157ca3c
Compare
src/sentry/workflow_engine/handlers/action/notification/notification.py
Outdated
Show resolved
Hide resolved
src/sentry/workflow_engine/handlers/action/notification/issue_alert.py
Outdated
Show resolved
Hide resolved
futures = cls.get_rule_futures(job, rule, notification_uuid) | ||
|
||
# Execute the futures | ||
cls.execute_futures(job, futures) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do these need to be separate? is it for having separate spans?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes but also- i want to decouple the logic here, i have a feeling when we move to notif platform, we will slowly remove each of these calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also makes it easier to test each of these steps with mock data, so I've been encouraging this kind of decoupling. It can also allow us to add metrics surrounding each call to see what the rough failure rate is for each step.
detector: Detector, | ||
) -> None: | ||
""" | ||
This method will create a rule instance from the Action model, and then invoke the legacy registry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not seeing the use of any registry here, maybe that's what colleen was asking about?
src/sentry/workflow_engine/handlers/action/notification/notification.py
Outdated
Show resolved
Hide resolved
""" | ||
|
||
rule = Rule( | ||
id=detector.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remind me why this needs to be detector.id
? is it possible that this collides with an existing Rule
id?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually i need this to be action.id
so i can fetch the correct action in our slack threads implementation & so i build links correctly. this won't collide with anything since this will be a rule instance not saved in the DB.
mock_safe_execute.assert_called_once_with(mock_callback, self.job["event"], mock_futures) | ||
|
||
|
||
class TestDiscordIssueAlertHandler(BaseWorkflowTest): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we test that the correct after() method is called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't need to. activate_downstream_actions
is well tested and as long as we are certain that the rule action id we are sending it (for example, "sentry.integrations.discord.notify_action.DiscordNotifyServiceAction"
for Discord), we can be certain it will call the correct after method
@@ -2,4 +2,4 @@ | |||
"NotificationActionHandler", | |||
] | |||
|
|||
from .notification import NotificationActionHandler | |||
from .notification.notification import NotificationActionHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this file path a little confusing, since notification/notification doesn't tell me a lot about what's in this module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updating to handler
futures = cls.get_rule_futures(job, rule, notification_uuid) | ||
|
||
# Execute the futures | ||
cls.execute_futures(job, futures) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also makes it easier to test each of these steps with mock data, so I've been encouraging this kind of decoupling. It can also allow us to add metrics surrounding each call to see what the rough failure rate is for each step.
logger.exception( | ||
"No issue alert handler found for action type: %s", | ||
action.type, | ||
extra={"action_id": action.id}, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this swallow the exception? Does the caller assume these calls never fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point, let me raise exceptions, we can swallow if needed later
This PR introduces a new ABC which we will inherit and create new handlers to invoke the legacy issue alert registry.
Basically after the notification action determines that it needs to invoke the issue alert registry (rule registry), it will call
BaseIssueAlertHandler.invoke_legacy_registry
which will shape the data into a rule instance and thensafe_execute
the callback returned by theRuleFuture
.the method mentioned above is based on
sentry/src/sentry/rules/processing/processor.py
Lines 147 to 184 in aa95456
spec
closes https://getsentry.atlassian.net/browse/ACI-150