Skip to main content

Overview

NuGet.Services.Storage is a shared infrastructure library that provides a uniform, URI-addressed storage abstraction used across all NuGet back-end services. It hides the differences between Azure Blob Storage, Azure Queue Storage, and the local file system behind common interfaces (IStorage, IStorageQueue, IBlobLeaseService), allowing service code to remain storage-agnostic and easily testable. The library also bundles:
  • An aggregate (fan-out) storage pattern for writing to a primary store while simultaneously mirroring to one or more secondary stores.
  • Azure Blob lease primitives for distributed locking.
  • Typed queue messaging with pluggable JSON serialization.

Blob Storage

Azure Block Blob read/write/delete with optional gzip compression, metadata, and server-side copy.

Queue Storage

Azure Storage Queue integration with typed message serialization and approximate-count support.

Blob Leases

Distributed locking via Azure Blob lease API — acquire, renew, and release with 15–60 s TTL.

File Storage

Local file-system backend implementing the same IStorage interface, used for local development and testing.

Role in the NuGetGallery System

This library sits at the infrastructure layer, consumed by catalog workers, search indexers, package processing pipelines, and other back-end jobs. Nothing in this library is web-request-specific; it is purely a server-side concern.
  • Services depend on IStorage / IStorageFactorynever on AzureStorage directly — enabling local-file substitution in unit tests.
  • AggregateStorage lets catalog publishing write to multiple CDN origins in a single logical call.
  • BlobLeaseService is used by job schedulers to prevent concurrent execution of singleton jobs.

Key Files and Classes


Dependencies

NuGet Package References

Internal Project References

None — this is a leaf library with no internal project dependencies. It is consumed by other projects in the solution.

Notable Patterns and Implementation Details

Template Method PatternStorage (abstract base) implements all public interface methods and delegates to protected OnSave, OnLoad, OnDelete, OnCopyAsync hooks. Subclasses only override the hooks. This guarantees consistent logging, exception wrapping, and operation counters (SaveCount, LoadCount, DeleteCount) across all backends.
Automatic gzip compressionAzureStorage has a CompressContent boolean property. When set, OnSave wraps the content stream in a GZipStream before uploading and sets Content-Encoding: gzip. On load, the encoding header is inspected and the stream is transparently decompressed.
AggregateStorage copy and SetMetadata are not implemented. Both OnCopyAsync and SetMetadataAsync throw NotImplementedException on AggregateStorage. Code that fans out to secondary stores cannot currently use server-side blob copy or metadata mutation.
FileStorage has partial implementation gaps. ListTopLevelAsync, OnCopyAsync, and SetMetadataAsync all throw NotImplementedException on FileStorage. It is intended only for local development, not production use.
Blob lease TTL constraintBlobLeaseService enforces Azure’s hard limits: lease duration must be between 15 and 60 seconds inclusive. If the blob targeted for the lease does not exist and createBlobsWhenMissing is true (the default), the service creates an empty zero-byte blob to serve as the lock resource.
Auth flexibility in BlobServiceClientFactory — The factory supports three authentication modes selected at construction time: connection string, TokenCredential (Managed Identity / service principal), or anonymous. This makes it straightforward to swap auth strategies per environment without changing consumer code.
Queue visibility timeout is hardcoded to 5 minutes in AzureStorageQueue. After GetNextAsync is called, the message becomes invisible to other consumers for 5 minutes. Callers must call RemoveAsync within that window or the message will reappear.
URI-relative addressing — All storage operations use absolute Uri values resolved via ResolveUri(string relativeUri). The base address always has a trailing / enforced at construction time, so relative paths concatenate safely without double-slash issues.