mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 05:00:07 +00:00
feat(ops): include per-orphan tenant/session breakdown in OrphanEventsCleaned
The OrphanEventsCleaned ops-event only persisted counts; the per-orphan
detail (which tenant/session lost lingering events) was logged via worker
LogInformation, which does not reach App Insights, so it was effectively
lost. Enrich the event details with a capped (top-50 by EventCount) list of
{tenantId, sessionId, eventCount} plus a detailsTruncated flag, reporting the
actual rows deleted. Keeps the Table row under the 32KB property limit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -709,6 +709,9 @@ namespace AutopilotMonitor.Functions.Services
|
||||
|
||||
int totalEventsDeleted = 0;
|
||||
int sessionsCleanedUp = 0;
|
||||
// Per-orphan breakdown for the OpsEvent so the dashboard shows which tenant/session
|
||||
// was cleaned (the worker LogInformation below does not reach App Insights).
|
||||
var cleanedOrphans = new List<OrphanedEventSession>();
|
||||
|
||||
foreach (var orphan in orphans)
|
||||
{
|
||||
@@ -719,6 +722,14 @@ namespace AutopilotMonitor.Functions.Services
|
||||
|
||||
totalEventsDeleted += deletedEvents;
|
||||
sessionsCleanedUp++;
|
||||
// Report the actual rows deleted (may differ from the index's recorded count).
|
||||
cleanedOrphans.Add(new OrphanedEventSession
|
||||
{
|
||||
TenantId = orphan.TenantId,
|
||||
SessionId = orphan.SessionId,
|
||||
LastIngestAt = orphan.LastIngestAt,
|
||||
EventCount = deletedEvents
|
||||
});
|
||||
|
||||
_logger.LogInformation(
|
||||
"Cleaned orphan: TenantId={TenantId}, SessionId={SessionId}, Events={Events}",
|
||||
@@ -736,7 +747,7 @@ namespace AutopilotMonitor.Functions.Services
|
||||
"Orphaned events cleanup completed in {Ms}ms: {Sessions} sessions, {Events} events deleted",
|
||||
sw.ElapsedMilliseconds, sessionsCleanedUp, totalEventsDeleted);
|
||||
|
||||
await _opsEventService.RecordOrphanEventsCleanedAsync(sessionsCleanedUp, totalEventsDeleted);
|
||||
await _opsEventService.RecordOrphanEventsCleanedAsync(sessionsCleanedUp, totalEventsDeleted, cleanedOrphans);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using AutopilotMonitor.Shared.DataAccess;
|
||||
@@ -76,10 +78,38 @@ namespace AutopilotMonitor.Functions.Services
|
||||
$"Cleaned up {deletedCount} ops events older than {retentionDays} days",
|
||||
null, "System.Maintenance", new { deletedCount, retentionDays });
|
||||
|
||||
public Task RecordOrphanEventsCleanedAsync(int orphanSessions, int totalEventsDeleted)
|
||||
=> WriteAsync(OpsEventCategory.Maintenance, "OrphanEventsCleaned", OpsEventSeverity.Warning,
|
||||
/// <summary>
|
||||
/// Records the orphan-cleanup result. <paramref name="cleanedOrphans"/> carries the
|
||||
/// per-session breakdown (which tenant + session lost lingering events and how many),
|
||||
/// so the ops dashboard shows *what* was cleaned, not just a count. The list is capped
|
||||
/// to keep the OpsEvents Table row under the per-property (32 KB) / entity (~1 MB) limits;
|
||||
/// the full count always survives in <c>orphanSessions</c> and a <c>detailsTruncated</c>
|
||||
/// flag flags any clipping. Note: an "orphan" is a session row that no longer exists while
|
||||
/// its events lingered past the 24h grace — the tenant itself usually still exists.
|
||||
/// </summary>
|
||||
public Task RecordOrphanEventsCleanedAsync(int orphanSessions, int totalEventsDeleted,
|
||||
IReadOnlyList<OrphanedEventSession>? cleanedOrphans = null)
|
||||
{
|
||||
const int maxDetailRows = 50;
|
||||
|
||||
var orphanList = cleanedOrphans ?? Array.Empty<OrphanedEventSession>();
|
||||
var detail = orphanList
|
||||
.OrderByDescending(o => o.EventCount)
|
||||
.Take(maxDetailRows)
|
||||
.Select(o => new { tenantId = o.TenantId, sessionId = o.SessionId, eventCount = o.EventCount })
|
||||
.ToList();
|
||||
|
||||
return WriteAsync(OpsEventCategory.Maintenance, "OrphanEventsCleaned", OpsEventSeverity.Warning,
|
||||
$"Cleaned {totalEventsDeleted} orphaned events across {orphanSessions} sessions",
|
||||
null, "System.Maintenance", new { orphanSessions, totalEventsDeleted });
|
||||
null, "System.Maintenance",
|
||||
new
|
||||
{
|
||||
orphanSessions,
|
||||
totalEventsDeleted,
|
||||
orphans = detail,
|
||||
detailsTruncated = orphanList.Count > maxDetailRows
|
||||
});
|
||||
}
|
||||
|
||||
// ── Cascade-Delete Maintenance (Plan §5 PR6 / §16 R14) ─────────────────
|
||||
// Four event types dispatched by SessionDeletionMaintenanceFunction. Each is also
|
||||
|
||||
Reference in New Issue
Block a user