Skip to main content

Overview

Gallery.Maintenance is a console executable background job that performs periodic housekeeping operations directly against the NuGet Gallery SQL database. It follows the standard NuGet Jobs pattern, extending JsonConfigurationJob from NuGet.Jobs.Common and driven by JobRunner, which handles the run loop, Application Insights telemetry, sleep intervals, and optional continuous-run vs. run-once behavior. The job’s architecture is built around an extensible MaintenanceTask abstract base class. On each run, Job uses reflection to discover every concrete, non-abstract subclass of MaintenanceTask in its own assembly, instantiates each one with a typed ILogger<T>, and executes them sequentially. If any task throws, its failure is recorded and the job continues running the remaining tasks, then throws a summary exception at the end to signal an unhealthy exit to the job runner. This means a single broken task does not prevent other maintenance work from completing. Currently the only implemented task is DeleteExpiredApiKeysTask, which queries the Credentials and Scopes tables for apikey.verify.* and apikey.v5 credential types whose Expires timestamp is in the past, logs each found record, then deletes the matching rows from both tables within a SQL transaction.

Role in System

Task Discovery via Reflection

Job.GetMaintenanceTasks() scans its own assembly at runtime for all concrete MaintenanceTask subclasses. Adding a new maintenance operation only requires creating a new class — no registration step needed.

Fault-Tolerant Orchestration

Individual task failures are caught, logged, and deferred. All tasks run on every iteration; a summary exception is raised afterwards only if at least one task failed.

Transactional Credential Cleanup

DeleteExpiredApiKeysTask deletes expired credentials and their associated scopes atomically within a SQL transaction, with up to 3 retries on the SELECT and a 5-minute command timeout.

Windows Service Deployment

Packaged as a NuGet package with PowerShell pre/post deploy scripts that use NSSM to register, configure automatic restart, and start/stop the job as a Windows service.

Key Files and Classes

Dependencies

NuGet Package References

Internal Project References

Notable Patterns and Implementation Details

New maintenance tasks are added by creating a new MaintenanceTask subclass with a constructor that accepts ILogger<TTask>. No manual registration is required — Job.GetMaintenanceTasks() discovers subclasses automatically using Assembly.GetTypes(). The constructor signature requirement is enforced at runtime via reflection; a missing or mismatched constructor will throw a NullReferenceException when the task is being instantiated.
DeleteExpiredApiKeysTask builds its DELETE query by formatting parameterized placeholder names into a string with string.Format. This pattern triggers CA2100 (“Review SQL queries for security vulnerabilities”) and is suppressed with a #pragma warning disable comment. The values are passed as SqlParameter objects with explicit SqlDbType.Int typing, so there is no actual injection risk, but the suppression is worth noting for future reviewers.
The row-count after the DELETE is validated against expectedRowCount = expiredApiKeys.Count() * 2 (one credential row plus one scope row per key). If the actual deleted row count does not match, the task throws an exception. This assumes a strict 1:1 relationship between credentials and scopes for the targeted credential types, which must remain true for the task to complete cleanly.
The job targets net472 and is deployed as a Windows service managed by NSSM. NSSM is bundled directly in the Scripts/ folder and is included in the NuGet package output, so no separate NSSM installation is required on the target host. The service is configured for automatic restart with a 5-second delay (sc.exe failure ... actions= restart/5000).

SQL Queries in DeleteExpiredApiKeysTask

SELECT (with 3-retry wrapper, 5-minute timeout):
DELETE (inside a transaction, parameterized key list):