Skip to content

Commit f313a03

Browse files
bruno-garciaclaude
andauthored
fix: delete stale package_downloads rows when removing deleted packages (#440)
* fix: delete stale package_downloads rows when removing deleted packages When the worker detects a deleted/unlisted package (NuGet API returns null), it removes the catalog entry but leaves the package_downloads record behind. The orphaned row keeps being re-queued by the publisher every day since its LatestDownloadCountCheckedUtc is never updated. In integration tests this causes WaitForAllPackagesProcessed to time out because GetUnprocessedPackageIds never reaches zero. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * address review: wrap both deletes in a transaction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: finish Sentry span only after transaction commit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 45554e6 commit f313a03

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

src/NuGetTrends.Scheduler/DailyDownloadWorker.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,16 +423,30 @@ await _clickHouseService.InsertDailyDownloadsAsync(
423423
// Handle deleted packages (batch query to avoid N+1)
424424
if (deletedPackageIds.Count > 0)
425425
{
426-
var deleteSpan = StartDbSpan(parentSpan, "DELETE FROM package_details_catalog_leafs WHERE package_id_lowered IN (...)", "postgresql", "DELETE");
426+
var loweredIds = deletedPackageIds.Select(id => id.ToLowerInvariant()).Distinct().ToList();
427+
428+
var deleteSpan = StartDbSpan(parentSpan,
429+
"DELETE FROM package_details_catalog_leafs, package_downloads WHERE package_id_lowered IN (...)",
430+
"postgresql", "DELETE");
427431
deleteSpan.SetTag("count", deletedPackageIds.Count.ToString());
428432
try
429433
{
430-
var loweredIds = deletedPackageIds.Select(id => id.ToLowerInvariant()).Distinct().ToList();
431-
var deletedCount = await context.PackageDetailsCatalogLeafs
434+
await using var transaction = await context.Database.BeginTransactionAsync(_cancellationTokenSource.Token);
435+
436+
var deletedCatalogCount = await context.PackageDetailsCatalogLeafs
437+
.Where(p => loweredIds.Contains(p.PackageIdLowered))
438+
.ExecuteDeleteAsync(_cancellationTokenSource.Token);
439+
440+
// Also remove stale package_downloads records so the publisher
441+
// doesn't keep re-queuing packages that no longer exist.
442+
var deletedDownloadsCount = await context.PackageDownloads
432443
.Where(p => loweredIds.Contains(p.PackageIdLowered))
433444
.ExecuteDeleteAsync(_cancellationTokenSource.Token);
434445

435-
deleteSpan.SetData("db.rows_affected", deletedCount);
446+
await transaction.CommitAsync(_cancellationTokenSource.Token);
447+
448+
deleteSpan.SetData("db.catalog_rows_affected", deletedCatalogCount);
449+
deleteSpan.SetData("db.downloads_rows_affected", deletedDownloadsCount);
436450
deleteSpan.Finish(SpanStatus.Ok);
437451
}
438452
catch (Exception e)

0 commit comments

Comments
 (0)