Skip to content

feat: add Deleted counter to usage metrics#2253

Draft
niemyjski wants to merge 1 commit into
mainfrom
niemyjski/feature-deleted-usage-metrics
Draft

feat: add Deleted counter to usage metrics#2253
niemyjski wants to merge 1 commit into
mainfrom
niemyjski/feature-deleted-usage-metrics

Conversation

@niemyjski
Copy link
Copy Markdown
Member

Why

Until now there was no way to see how many events were explicitly deleted via the API, project reset, or soft-delete cleanup. This makes it impossible to distinguish "events never arrived" from "events were intentionally removed" when analyzing usage. This PR adds a first-class Deleted counter alongside the existing Total, Blocked, Discarded, and TooBig counters -- without touching billing, plan limits, or admission control.

What changed

Backend

  • UsageInfo / UsageHourInfo -- added long Deleted property to both records (backwards-compatible as ES stores it as a numeric type).
  • AppDiagnostics -- added EventsDeleted as a Counter<long> for OTel metrics.
  • UsageService -- new IncrementDeletedAsync(organizationId, projectId, eventCount) that pipelines all Redis operations via Task.WhenAll; cache key pattern: usage:{bucket}:{orgId}[:projectId]:deleted. Save/flush/read logic updated to include the Deleted bucket (mirrors the other counters exactly).
  • EventController -- injects UsageService; DeleteModelsAsync groups loaded events once (single GroupBy), calls base.DeleteModelsAsync, then calls IncrementDeletedAsync per org/project group inside a try/catch so a tracking failure never blocks the delete response.
  • ResetProjectDataWorkItemHandler -- calls IncrementDeletedAsync with the raw event count returned by the delete query (no truncation cast).
  • CleanupDataJob -- injects UsageService; RemoveProjectsAsync instruments deleted; RemoveStacksAsync accepts a trackDeletedUsage flag (true for soft-delete cleanup, false for retention enforcement) with proportional per-project distribution using long arithmetic. RemoveOrganizationAsync intentionally does NOT track -- the org is being hard-deleted so the data has no consumer.

Frontend (Svelte 5)

  • Generated types (api.ts, schemas.ts) updated to include deleted: number.
  • Org and project usage pages: Deleted series added to charts, dead variable alias removed.

Frontend (Angular legacy)

  • Org and project manage controllers: Deleted series added at the correct index.

Test coverage

Test class New tests
UsageServiceTests 6 new (CanIncrementDeletedAsync, flush, read-back, isolation, zero-guard, zero-event-count guard)
EventControllerTests 2 integration tests: single delete and multi-event delete with org+project flush assertion
CleanupDataJobTests 5 tests: soft-deleted project tracks usage, empty project no-op, soft-deleted stack tracks usage, multi-project proportional distribution, retention enforcement does NOT track
ResetProjectDataWorkItemHandlerTests 5 tests: basic tracking, empty project, multiple stacks, project isolation, no billing impact

Existing tests updated to assert Deleted == 0 where appropriate.

Key design decisions

  • Retention deletions are NOT tracked. RemoveStacksAsync is called with trackDeletedUsage=false for retention enforcement paths. Only explicit user-initiated deletes and soft-delete cleanup show up as Deleted usage.
  • long not int. High-volume deployments can delete millions of events; int would overflow. All cache reads use GetAsync<long>.
  • No Math.Min truncation. Proportional distribution in RemoveStacksAsync uses pure long arithmetic; the last group absorbs any remainder to prevent event count loss.
  • No billing changes. Deleted is a display-only counter. It does not affect GetEventsLeftAsync, plan enforcement, or Stripe metering.

Comment on lines +1480 to +1483
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to increment deleted usage metrics");
}
- Add Deleted property to UsageInfo and UsageHourInfo models
- Add EventsDeleted counter to AppDiagnostics
- Add IncrementDeletedAsync to UsageService with cache key, save, and
  GetUsageAsync support
- Update EventController.DeleteModelsAsync to track deleted event counts
- Update ResetProjectDataWorkItemHandler to track deleted events on
  project reset
- Update Svelte org/project usage pages to chart Deleted series
- Update Angular org/project manage controllers to chart Deleted series
- Update generated TypeScript API types
- Add comprehensive UsageService tests for deleted metrics
- Add EventController integration tests for delete usage tracking

Does not change plan limits, billing, or admission control.
Does not instrument retention cleanup, bot cleanup, or orphan cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@niemyjski niemyjski force-pushed the niemyjski/feature-deleted-usage-metrics branch from 77a4c37 to bbd89a4 Compare May 25, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant