Repository navigation
Add AzureBlobPersistenceProvider (RiftCache.Providers.Azure) - #13
Merged
Merged
Conversation
Second of the two Azure reference providers -- last remaining Phase 1 item after this is the Bicep deployment. Unlike Key Vault, Azure Blob Storage has an official local emulator (Azurite), and CONTRIBUTING.md explicitly requires provider tests to run against a local emulator in CI, not just unit tests. So this one gets both: fast unit tests against a hand-faked BlobContainerClient (same subclassing approach as FakeSecretClient, no mocking framework), and real integration tests against Azurite, now wired into .github/workflows/ci.yml as a service container. Design: - Blob content = the cached bytes directly, inspectable, no envelope format. AbsoluteExpiration/SlidingExpiration live in blob metadata instead (round-trip ISO 8601 / seconds). LastAccessed isn't persisted -- InMemoryCacheStore re-Touch()es on promotion from persistence, so a stored value would never be read. - Blob name is the cache key, Uri.EscapeDataString-encoded -- reusing the exact encoding CacheEndpoints.cs and RiftCacheClient.cs already use for the same keys. - Doesn't create the container, same stance as AzureKeyVaultSecretProvider not creating the vault -- provisioning is the Bicep work's job. - AddAzureBlobPersistenceProvider(containerUri, credential?) mirrors the Key Vault extension's shape exactly: Managed-Identity-first via DefaultAzureCredential. Verified locally end to end, not just assumed: ran Azurite via Podman, hit a real connectivity snag first (port bound IPv6-loopback-only by the Podman machine's forwarder -- explicit `-p 127.0.0.1:10000:10000` fixed it), then confirmed all three integration tests pass against the live emulator before wiring CI. GitHub Actions runners are Linux, so that IPv4/IPv6 quirk is Windows/Podman-specific and won't recur there. Also reflected over the installed 12.29.1 assembly (same lesson from the Key Vault work: don't assume Azure SDK overload/dispatch behavior) to confirm BlobClient.UploadAsync/DownloadContentAsync/DeleteIfExistsAsync each resolve to a single unambiguous virtual overload -- no repeat of the GetSecretAsync surprise. Also fixed a stale doc-comment reference to ARCHITECTURE_NOTES.md (renamed to ARCHITECTURE.md several PRs ago) noticed while re-reading IPersistenceProvider.cs, and flipped Blob Storage from "not built yet" to available in docs/providers/azure.md and ROADMAP.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR checks failed: "The API version 2026-06-06 is not supported by Azurite." The SDK's default service version floats to whatever's newest (12.29.1's default is V2026_06_06); Azurite lags behind and rejects anything it doesn't recognize unless --skipApiVersionCheck is passed. My local Podman testing happened to include that flag, masking this exact failure -- GitHub Actions' `services:` block has no command-override field to pass it to a service container, so it must be fixed on the client side instead. Reproduced locally first (Azurite without --skipApiVersionCheck) to confirm the real cause before touching anything, then pinned both the integration tests and the production AddAzureBlobPersistenceProvider extension to BlobClientOptions.ServiceVersion.V2024_08_04. Applied to production too, not just tests: real Azure Storage supports old API versions for years so there's no functional downside, and it means AddAzureBlobPersistenceProvider works against a local Azurite instance out of the box for anyone testing before deploying to real Azure -- not just this repo's own test suite. Verified against a strict (no-skip-flag) local Azurite before pushing: all 3 integration tests now pass under the same condition CI hits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second of the two Azure reference providers -- last remaining Phase 1 item after this is the Bicep deployment.
Unlike Key Vault, Azure Blob Storage has an official local emulator (Azurite), and CONTRIBUTING.md explicitly requires provider tests to run against a local emulator in CI, not just unit tests. So this one gets both: fast unit tests against a hand-faked BlobContainerClient (same subclassing approach as FakeSecretClient, no mocking framework), and real integration tests against Azurite, now wired into .github/workflows/ci.yml as a service container.
Design:
Verified locally end to end, not just assumed: ran Azurite via Podman, hit a real connectivity snag first (port bound IPv6-loopback-only by the Podman machine's forwarder -- explicit
-p 127.0.0.1:10000:10000fixed it), then confirmed all three integration tests pass against the live emulator before wiring CI. GitHub Actions runners are Linux, so that IPv4/IPv6 quirk is Windows/Podman-specific and won't recur there.Also reflected over the installed 12.29.1 assembly (same lesson from the Key Vault work: don't assume Azure SDK overload/dispatch behavior) to confirm BlobClient.UploadAsync/DownloadContentAsync/DeleteIfExistsAsync each resolve to a single unambiguous virtual overload -- no repeat of the GetSecretAsync surprise.
Also fixed a stale doc-comment reference to ARCHITECTURE_NOTES.md (renamed to ARCHITECTURE.md several PRs ago) noticed while re-reading IPersistenceProvider.cs, and flipped Blob Storage from "not built yet" to available in docs/providers/azure.md and ROADMAP.md.