Skip to content

Files purge jobs skip tenants with a dedicated database and lose the storage-quota refund #1418

Description

@iammukeshm

Found while fixing #1411 (#1413), by reading the code.

PurgeOrphanedFilesJob and PurgeDeletedFilesJob (Files, Hangfire recurring) run without a tenant context. They don't crash the way AuditRetentionJob did, because they query FileAssets with IgnoreQueryFilters(). But:

  1. Tenants with a dedicated database are never purged. With no tenant resolved, FilesDbContext connects to the default database, so only the shared database's rows (and their blobs) get cleaned. File rows in a tenant's own database stay forever.
  2. Storage-quota refunds are lost. PurgeDeletedFilesJob calls quotas.RecordAsync("", ...) with an empty tenant, so the purged bytes are never credited back to the owning tenant. The code comment there admits this.

Fix

Same pattern as #1406/#1413: iterate IMultiTenantStore<AppTenantInfo>.GetAllAsync(), open a scope per tenant, set IMultiTenantContextSetter, run the purge against that tenant's FilesDbContext, and refund the quota under that tenant's id. Catch and log per tenant. Drop IgnoreQueryFilters() for the tenant filter (keep it only for soft-delete where it's needed). Add integration tests that seed purgeable files in two tenants and assert per-tenant purge and quota refund.

Activity

  1. added a commit that references this issue on Sep 30, 2026
    72c5d0f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions