Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2021)

Unified Diff: dashboard/dashboard/models/alert_group.py

Issue 2650683007: Dashboard - Remove pre-hook logic from alert.py. (Closed)
Patch Set: Fix race in delete. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « dashboard/dashboard/models/alert.py ('k') | dashboard/dashboard/models/alert_group_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: dashboard/dashboard/models/alert_group.py
diff --git a/dashboard/dashboard/models/alert_group.py b/dashboard/dashboard/models/alert_group.py
index 8d344f3360f4fae1cda002d81e27cc8f84ef8cbd..55de7a241a98abf90ed9352fa89f09e03cd94d7e 100644
--- a/dashboard/dashboard/models/alert_group.py
+++ b/dashboard/dashboard/models/alert_group.py
@@ -39,13 +39,140 @@ class AlertGroup(ndb.Model):
Args:
grouped_alerts: Alert entities that belong to this group. These
are only given here so that they don't need to be fetched.
+ Returns:
+ True if modified, False otherwise.
"""
min_rev_range = utils.MinimumAlertRange(grouped_alerts)
start, end = min_rev_range if min_rev_range else (None, None)
if self.start_revision != start or self.end_revision != end:
self.start_revision = start
self.end_revision = end
- self.put()
+ return True
+ return False
+
+
+def ModifyAlertsAndAssociatedGroups(alert_entities, **kwargs):
+ """Modifies a list of alerts and their corresponding groups.
+
+ There's some book-keeping that needs to be done when modifying an alert,
+ specifically when modifying either the bug_id or it's revision range. These
+ can potentially trigger modifications or even deletions of AlertGroups.
+
+ Args:
+ alert_entities: A list of alert entities to modify.
+ bug_id: An optional bug_id to set.
+ start_revision: An optional start_revision to set.
+ end_revision: An optional end_revision to set.
+ """
+ modified_groups = {}
+ modified_alerts = []
+ deleted_groups = []
+
+ valid_args = ['bug_id', 'start_revision', 'end_revision']
+
+ # 1st pass, for each alert that's modified, kick off an async get for
+ # it's group.
+ group_futures = {}
+ valid_alerts = []
+ for a in alert_entities:
+ if not a.group or a.group.kind() != 'AlertGroup':
+ a.group = None
+
+ modified = False
+
+ # We use kwargs instead of default args since None is actually a valid
+ # value to set and using kwargs let's us easily distinguish betwen
+ # setting None, and not passing that arg at all.
+ for v in valid_args:
+ if v in kwargs:
+ if getattr(a, v) != kwargs[v]:
+ setattr(a, v, kwargs[v])
+ modified = True
+
+ if not modified:
+ continue
+
+ modified_alerts.append(a)
+
+ if not a.group:
+ continue
+
+ if not a.group.id() in group_futures:
+ group_futures[a.group.id()] = a.group.get_async()
+
+ valid_alerts.append(a)
+
+ # 2nd pass, for each group, kick off async queries for any other alerts in
+ # the same group.
+ alert_entities = valid_alerts
+ valid_alerts = []
+ grouped_alerts_futures = {}
+ for a in alert_entities:
+ group_future = group_futures[a.group.id()]
+ group_entity = group_future.get_result()
+ if not group_entity:
+ continue
+
+ valid_alerts.append(a)
+
+ if a.group.id() in grouped_alerts_futures:
+ continue
+
+ alert_cls = a.__class__
+ grouped_alerts_future = alert_cls.query(
+ alert_cls.group == group_entity.key).fetch_async()
+ grouped_alerts_futures[a.group.id()] = grouped_alerts_future
+
+ # 3rd pass, modify groups
+ alert_entities = valid_alerts
+ grouped_alerts_cache = {}
+ for a in alert_entities:
+ # We cache these rather than grab get_result() each time because we may
+ # modify them in a previous iteration and we want those modifications.
+ if a.group.id() in grouped_alerts_cache:
+ group_entity, grouped_alerts = grouped_alerts_cache[a.group.id()]
+ else:
+ group_entity = group_futures[a.group.id()].get_result()
+ grouped_alerts = grouped_alerts_futures[a.group.id()].get_result()
+ grouped_alerts_cache[a.group.id()] = (group_entity, grouped_alerts)
+
+ if not a in grouped_alerts:
+ grouped_alerts.append(a)
+
+ if 'bug_id' in kwargs:
+ bug_id = kwargs['bug_id']
+ # The alert has been assigned a real bug ID.
+ # Update the group bug ID if necessary.
+ if bug_id > 0 and group_entity.bug_id != bug_id:
+ group_entity.bug_id = bug_id
+ modified_groups[group_entity.key.id()] = group_entity
+ # The bug has been marked invalid/ignored. Kick it out of the group.
+ elif bug_id < 0 and bug_id is not None:
+ a.group = None
+ grouped_alerts.remove(a)
+ # The bug has been un-triaged. Update the group's bug ID if this is
+ # the only alert in the group.
+ elif bug_id is None and len(grouped_alerts) == 1:
+ group_entity.bug_id = None
+ modified_groups[group_entity.key.id()] = group_entity
+
+ if group_entity.UpdateRevisionRange(grouped_alerts):
+ modified_groups[group_entity.key.id()] = group_entity
+
+ # Do final pass to remove all empty groups. If we both delete the group and
+ # put() it back after modifications, it's a race as to which actually happens.
+ for k, (group_entity, grouped_alerts) in grouped_alerts_cache.iteritems():
+ if not grouped_alerts:
+ deleted_groups.append(group_entity.key)
+ if k in modified_groups:
+ del modified_groups[k]
+
+ modified_groups = modified_groups.values()
+
+ futures = ndb.delete_multi_async(deleted_groups)
+ futures.extend(ndb.put_multi_async(modified_alerts + modified_groups))
+
+ ndb.Future.wait_all(futures)
def GroupAlerts(alerts, test_suite, kind):
« no previous file with comments | « dashboard/dashboard/models/alert.py ('k') | dashboard/dashboard/models/alert_group_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698