Overview
ArchivePackages is a headless .NET Framework 4.7.2 console job that provides incremental backup of NuGet package binaries (.nupkg files). On each execution it:
- Reads a cursor timestamp from a
cursor.jsonblob stored in the destination container. - Queries the Gallery SQL database for all packages whose
PublishedorLastEditedtimestamp is newer than the cursor. - For each such package, triggers a server-side blob copy from the primary
packagescontainer into anng-backupscontainer on the destination storage account. - Advances the cursor to
max(LastEdited, Published)across the batch.
The job only initiates the blob copy — it calls
StartCopyAsync and does not wait for Azure Storage to complete the server-side transfer. Any process relying on a fully-consistent backup must account for in-flight copies.Role in System
Source
Azure Blob Storage —
packages container. Blob name format: {id}.{version}.nupkg (lowercased).Destination
Azure Blob Storage —
ng-backups container. Blob name format: packages/{id}/{version}/{url-encoded-hash}.nupkg.Cursor
cursor.json stored in the destination container. Contains a single ISO-8601 cursorDateTime key that advances after each successful batch.Deployment
Packaged as a NuGet package via
.nuspec; installed as a Windows Service using NSSM via Octopus Deploy PowerShell scripts.Key Files and Classes
| File | Class / Type | Purpose |
|---|---|---|
ArchivePackages.Job.cs | Job | Main job logic — reads cursor, queries DB, drives the archive loop for primary and optional secondary destinations |
ArchivePackages.Program.cs | Program | Entry point; constructs Job and hands off to JobRunner.Run() |
Configuration/InitializationConfiguration.cs | InitializationConfiguration | Strongly-typed config POCO bound from the Initialization JSON section |
JobEventSource.cs | JobEventSource | ETW event source (Outercurve-NuGet-Jobs-ArchivePackages) emitting structured events for every major operation |
PackageRef.cs | PackageRef | Plain data class representing a Dapper query row — holds Id, Version, Hash, LastEdited, Published |
Scripts/Functions.ps1 | — | PowerShell helpers Install-NuGetService / Uninstall-NuGetService used by deploy scripts |
Dependencies
NuGet Package References
| Package | Purpose |
|---|---|
WindowsAzure.Storage | Azure Blob Storage client — ICloudBlobContainer, StartCopyAsync, UploadFromStreamAsync |
Internal Project References
| Project | Key Contributions |
|---|---|
NuGet.Jobs.Common | JsonConfigurationJob base class, JobRunner, StorageHelpers (blob name formatting), GalleryDbConfiguration |
Transitive / Shared Dependencies
| Library | Role |
|---|---|
Autofac | DI container |
Dapper | Micro-ORM for the SQL query against Packages / PackageRegistrations |
Microsoft.Extensions.Configuration | JSON configuration loading with KeyVault secret injection |
Newtonsoft.Json | Parsing and updating cursor.json via JObject |
Configuration Reference
Notable Patterns and Implementation Details
Cursor-based incremental processing. The cursor is a single
cursorDateTime value persisted inside cursor.json in the destination container itself. A freshly created destination container requires a manually seeded cursor.json before the job will run successfully.Parallel tuple construction, sequential copy dispatch. The Dapper result list is projected into
(sourceBlobName, destBlobName) tuples using AsParallel().Select(...), but actual ArchivePackage calls are dispatched sequentially. The parallelism only benefits name-string computation, not I/O throughput.Blob name hash encoding. Destination blob names URL-encode the package hash (
WebUtility.UrlEncode(hash)). This is necessary because SHA-512 hashes are Base64 strings containing +, /, and = characters that are not safe in Azure blob path segments.