Skip to content

Commit d850b41

Browse files
fix(cippcore): harden cache cleanup memory use
Refactor reporting DB writes to keep memory bounded by batch size and switch orphan detection from a run-wide RowKey set to Timestamp-based evidence. This adds a skew margin option, skips undated rows instead of risking mass deletion, and only runs orphan cleanup after successful writes. Also rework audit-log ingestion tenant discovery to stream directly into HashSets with minimal property projection, removing Group-Object and large intermediate arrays that were triggering OutOfMemoryException under backlog load. Synced from CyberDrain/CIPP@453928c
1 parent 4e197d3 commit d850b41

2 files changed

Lines changed: 29 additions & 19 deletions

File tree

Modules/CIPPCore/Public/Add-CIPPDbItem.ps1

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@ function Add-CIPPDbItem {
2121

2222
[switch]$Count,
2323
[switch]$AddCount,
24-
[switch]$Append
24+
[switch]$Append,
25+
26+
[ValidateRange(0, 60)]
27+
[int]$SkewMarginMinutes = 5
2528
)
2629

2730
begin {
2831
$Table = Get-CippTable -tablename 'CippReportingDB'
29-
$Batch = [System.Collections.Generic.List[hashtable]]::new()
30-
$NewRowKeys = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
32+
$BatchSize = 100
33+
$Batch = [System.Collections.Generic.List[hashtable]]::new($BatchSize)
34+
$SeenInBatch = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
35+
$RunStartUtc = [DateTimeOffset]::UtcNow.AddMinutes(-$SkewMarginMinutes)
36+
3137
$TotalProcessed = 0
3238
# Cache regex instances so each row pays only the match cost, not regex compilation.
3339
# Two passes preserve the original semantics: path/wildcard chars → '_', control chars → stripped.
@@ -54,17 +60,18 @@ function Add-CIPPDbItem {
5460
if ($null -eq $Item) { continue }
5561
$ItemId = $Item.ExternalDirectoryObjectId ?? $Item.id ?? $Item.Identity ?? $Item.skuId ?? $Item.userPrincipalName ?? [guid]::NewGuid().ToString()
5662
$RowKey = $RowKeyControlRegex.Replace($RowKeyPathRegex.Replace("$Type-$ItemId", '_'), '')
57-
if ($NewRowKeys.Add($RowKey)) {
63+
if ($SeenInBatch.Add($RowKey)) {
5864
$Batch.Add(@{
5965
PartitionKey = $TenantFilter
6066
RowKey = $RowKey
6167
Data = [string]($Item | ConvertTo-Json -Depth 10 -Compress)
6268
Type = $Type
6369
})
64-
if ($Batch.Count -ge 500) {
70+
if ($Batch.Count -ge $BatchSize) {
6571
$null = Add-CIPPAzDataTableEntity @Table -Entity $Batch.ToArray() -Force
6672
$TotalProcessed += $Batch.Count
6773
$Batch.Clear()
74+
$SeenInBatch.Clear()
6875
}
6976
}
7077
}
@@ -76,17 +83,22 @@ function Add-CIPPDbItem {
7683
$TotalProcessed += $Batch.Count
7784
}
7885

79-
# Clean up orphaned rows (entities that no longer exist in the new dataset)
80-
if (-not $Count.IsPresent -and -not $Append.IsPresent) {
86+
# Clean up orphaned rows (entities that no longer exist in the new dataset).
87+
if (-not $Count.IsPresent -and -not $Append.IsPresent -and $TotalProcessed -gt 0) {
8188
$Filter = "PartitionKey eq '{0}' and RowKey ge '{1}-' and RowKey lt '{1}0'" -f $TenantFilter, $Type
82-
$Existing = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey, ETag, OriginalEntityId
89+
$Existing = Get-CIPPAzDataTableEntity @Table -Filter $Filter -Property PartitionKey, RowKey, ETag, OriginalEntityId, Timestamp
8390
if ($Existing) {
91+
$Undated = 0
8492
$Orphans = foreach ($Row in @($Existing)) {
8593
if ($Row.RowKey -eq "$Type-Count") { continue }
86-
$ParentKey = $Row.OriginalEntityId ?? $Row.RowKey
87-
if (-not $NewRowKeys.Contains($ParentKey)) {
88-
$Row
89-
}
94+
95+
$Stamp = $Row.Timestamp -as [datetimeoffset]
96+
if ($null -eq $Stamp) { $Undated++; continue }
97+
98+
if ($Stamp -lt $RunStartUtc) { $Row }
99+
}
100+
if ($Undated -gt 0) {
101+
Write-LogMessage -API 'CIPPDbItem' -tenant $TenantFilter -sev Warning -message "Skipped $Undated $Type row(s) with no readable Timestamp during orphan cleanup — not deleting without positive evidence"
90102
}
91103
if ($Orphans) {
92104
$null = Remove-AzDataTableEntity @Table -Entity @($Orphans) -Force

Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogIngestionV2.ps1

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,17 @@ function Start-AuditLogIngestionV2 {
2828
$Now = (Get-Date).ToUniversalTime()
2929

3030
# --- Download tenants: searches awaiting download (State = Created, due) ---
31-
$Created = @(Get-CIPPAzDataTableEntity @Ledger -Filter "State eq 'Created'")
32-
$DueCreated = $Created | Where-Object { -not $_.NextAttemptUtc -or ([datetimeoffset]$_.NextAttemptUtc).UtcDateTime -le $Now }
3331
$DownloadTenants = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
34-
foreach ($Name in @($DueCreated | Group-Object PartitionKey | ForEach-Object { $_.Name })) {
35-
if ($Name) { [void]$DownloadTenants.Add([string]$Name) }
32+
foreach ($Row in @(Get-CIPPAzDataTableEntity @Ledger -Filter "State eq 'Created'" -Property @('PartitionKey', 'NextAttemptUtc'))) {
33+
if ($Row.NextAttemptUtc -and ([datetimeoffset]$Row.NextAttemptUtc).UtcDateTime -gt $Now) { continue }
34+
if ($Row.PartitionKey) { [void]$DownloadTenants.Add([string]$Row.PartitionKey) }
3635
}
3736

3837
# --- Process-only tenants: rows pending in the webhook cache (downloaded, not yet processed) ---
3938
$CacheTable = Get-CippTable -TableName 'CacheWebhooks'
40-
$CacheRows = @(Get-CIPPAzDataTableEntity @CacheTable -Property @('PartitionKey', 'RowKey'))
4139
$CacheTenants = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
42-
foreach ($Name in @($CacheRows | Group-Object PartitionKey | ForEach-Object { $_.Name })) {
43-
if ($Name) { [void]$CacheTenants.Add([string]$Name) }
40+
foreach ($Row in @(Get-CIPPAzDataTableEntity @CacheTable -Property @('PartitionKey'))) {
41+
if ($Row.PartitionKey) { [void]$CacheTenants.Add([string]$Row.PartitionKey) }
4442
}
4543

4644
if ($DownloadTenants.Count -eq 0 -and $CacheTenants.Count -eq 0) {

0 commit comments

Comments
 (0)