Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 9207482

Browse files
author
Scott Bommarito
authored
Status Aggregator - Better detect noisy incidents and do not post messages for them (#549)
1 parent 009a1a6 commit 9207482

5 files changed

Lines changed: 47 additions & 10 deletions

File tree

src/StatusAggregator/EventUpdater.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,21 @@ public async Task<bool> UpdateEvent(EventEntity eventEntity, DateTime cursor)
6666
return false;
6767
}
6868

69-
var shouldDeactivate = !incidentsLinkedToEventQuery
70-
.Where(i => i.IsActive || i.MitigationTime > cursor - _eventEndDelay)
71-
.ToList()
72-
.Any();
69+
// We are querying twice here because table storage ignores rows where a column specified by a query is null.
70+
// MitigationTime is null when IsActive is true.
71+
// If we do not query separately here, rows where IsActive is true will be ignored in query results.
7372

73+
var hasActiveIncidents = incidentsLinkedToEventQuery
74+
.Where(i => i.IsActive)
75+
.ToList()
76+
.Any();
77+
78+
var hasRecentIncidents = incidentsLinkedToEventQuery
79+
.Where(i => i.MitigationTime > cursor - _eventEndDelay)
80+
.ToList()
81+
.Any();
82+
83+
var shouldDeactivate = !(hasActiveIncidents || hasRecentIncidents);
7484
if (shouldDeactivate)
7585
{
7686
_logger.LogInformation("Deactivating event because its incidents are inactive and too old.");

src/StatusAggregator/IncidentUpdater.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ namespace StatusAggregator
1616
public class IncidentUpdater : IIncidentUpdater
1717
{
1818
private readonly ITableWrapper _table;
19-
private readonly IEventUpdater _eventUpdater;
2019
private readonly IAggregateIncidentParser _aggregateIncidentParser;
2120
private readonly IIncidentApiClient _incidentApiClient;
2221
private readonly IIncidentFactory _incidentFactory;
@@ -26,15 +25,13 @@ public class IncidentUpdater : IIncidentUpdater
2625

2726
public IncidentUpdater(
2827
ITableWrapper table,
29-
IEventUpdater eventUpdater,
3028
IIncidentApiClient incidentApiClient,
3129
IAggregateIncidentParser aggregateIncidentParser,
3230
IIncidentFactory incidentFactory,
3331
StatusAggregatorConfiguration configuration,
3432
ILogger<IncidentUpdater> logger)
3533
{
3634
_table = table ?? throw new ArgumentNullException(nameof(table));
37-
_eventUpdater = eventUpdater ?? throw new ArgumentNullException(nameof(eventUpdater));
3835
_incidentApiClient = incidentApiClient ?? throw new ArgumentNullException(nameof(incidentApiClient));
3936
_aggregateIncidentParser = aggregateIncidentParser ?? throw new ArgumentNullException(nameof(aggregateIncidentParser));
4037
_incidentFactory = incidentFactory ?? throw new ArgumentNullException(nameof(incidentFactory));

src/StatusAggregator/Job.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private static CloudBlobContainer GetCloudBlobContainer(IComponentContext ctx, C
164164
}
165165

166166
private const int _defaultEventStartMessageDelayMinutes = 15;
167-
private const int _defaultEventEndDelayMinutes = 10;
167+
private const int _defaultEventEndDelayMinutes = 15;
168168
private const int _defaultEventVisibilityPeriod = 10;
169169

170170
private static void AddConfiguration(IServiceCollection serviceCollection, IDictionary<string, string> jobArgsDictionary)

src/StatusAggregator/MessageUpdater.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,32 @@ public async Task CreateMessageForEventStart(EventEntity eventEntity, DateTime c
4949
return;
5050
}
5151

52+
var incidentsLinkedToEventQuery = _table.GetIncidentsLinkedToEvent(eventEntity);
53+
54+
// We are querying twice here because table storage ignores rows where a column specified by a query is null.
55+
// MitigationTime is null when IsActive is true.
56+
// If we do not query separately here, rows where IsActive is true will be ignored in query results.
57+
58+
var hasCurrentlyActiveIncidents = incidentsLinkedToEventQuery
59+
.Where(i => i.IsActive)
60+
.ToList()
61+
.Any();
62+
63+
var hasIncidentsActiveAtCursorTime = incidentsLinkedToEventQuery
64+
.Where(i => i.MitigationTime >= cursor)
65+
.ToList()
66+
.Any();
67+
68+
var hasActiveIncidents = hasCurrentlyActiveIncidents || hasIncidentsActiveAtCursorTime;
69+
if (!hasActiveIncidents)
70+
{
71+
// If we haven't told customers about an event and it doesn't have any active incidents, it must have be linked to an incident that fired and was quickly mitigated.
72+
// This event will be deactivated if it is not linked to an active incident soon.
73+
// We will create a message when it is linked to another active incident.
74+
_logger.LogInformation("Event has no active incidents, cannot create message for its start.");
75+
return;
76+
}
77+
5278
if (TryGetContentsForMessageForEventStart(eventEntity, out var contents))
5379
{
5480
await CreateMessage(eventEntity, eventEntity.StartTime, contents);

src/StatusAggregator/StatusUpdater.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,26 @@ public async Task Update()
4545
await ProcessCursor($"{ManualCursorBaseName}{manualStatusChangeUpdater.Name}", manualStatusChangeUpdater.ProcessNewManualChanges);
4646
}
4747

48-
await ProcessCursor(IncidentCursorName, async (value) =>
48+
var incidentCursor = await ProcessCursor(IncidentCursorName, async (value) =>
4949
{
5050
await _incidentUpdater.RefreshActiveIncidents();
5151
return await _incidentUpdater.FetchNewIncidents(value);
5252
});
53+
54+
await _eventUpdater.UpdateActiveEvents(incidentCursor);
5355
}
5456
}
5557

56-
private async Task ProcessCursor(string name, Func<DateTime, Task<DateTime?>> processCursor)
58+
private async Task<DateTime> ProcessCursor(string name, Func<DateTime, Task<DateTime?>> processCursor)
5759
{
5860
var lastCursor = await _cursor.Get(name);
5961
var nextCursor = await processCursor(lastCursor);
6062
if (nextCursor.HasValue)
6163
{
6264
await _cursor.Set(name, nextCursor.Value);
6365
}
66+
67+
return nextCursor ?? lastCursor;
6468
}
6569
}
6670
}

0 commit comments

Comments
 (0)