"Might as well face it, you're addicted to blob..." [BlobStoreApi update adds container management, fragmented response handling, other improvements, and enhanced Amazon S3 support by Delay.Web.Helpers!]
As part of my previous post announcing the Delay.Web.Helpers
assembly for ASP.NET and introducing the AmazonS3Storage
class to enable easy Amazon Simple Storage Service (S3) blob access from MVC/Razor/WebMatrix web pages, I made some changes to the BlobStoreApi.cs
file it built on top of. BlobStoreApi.cs
was originally released as part of my BlobStore sample for Silverlight and BlobStoreOnWindowsPhone sample for Windows Phone 7; I'd made a note to update those two samples with the latest version of the code, but intended to blog about a few other things first...
Fragmented response handling
That's when I was contacted by Steve Marx of the Azure team to see if I could help out Erik Petersen of the Windows Phone team who was suddenly seeing a problem when using BlobStoreApi
: calls to GetBlobInfos
were returning only some of the blobs instead of the complete list. Fortunately, Steve knew what was wrong without even looking - he'd blogged about the underlying cause over a year ago! Although Windows Azure can return up to 5,000 blobs in response to a query, it may decide (at any time) to return fewer (possibly as few as 0!) and instead include a token for getting the rest of the results from a subsequent call. This behavior is deterministic according to where the data is stored in the Azure cloud, but from the point of view of a developer it's probably safest to assume it's random. :)
So something had changed for Erik's container and now he was getting partial results because my BlobStoreApi
code didn't know what to do with the "fragmented response" it had started getting. Although I'd like to blame the documentation for not making it clear that even very small responses could be broken up, I really should have supported fragmented responses anyway so users would be able to work with more than 5,000 blobs. Mea culpa...
I definitely wanted to add BlobStoreApi
support for fragmented Azure responses, but what about S3? Did my code have the same problem there? According to the documentation: yes! Although I don't see mention of the same "random" fragmentation behavior Azure has, S3 enumerations are limited to 1,000 blobs at a time, so that code needs the same update in order to support customer scenarios with lots of blobs. Fortunately, the mechanism both services use to implement fragmentation is quite similar and I was able to add most of the new code to the common RestBlobStoreClient
base class and then refine it with service-specific tweaks in the sub-classes AzureBlobStoreClient
and S3BlobStoreClient
.
Fortunately, the extra work to support fragmented responses is all handled internally and is therefore invisible to the developer. As such, it requires no changes to existing applications beyond updating to the new BlobStoreApi.cs
file and recompiling!
Container support
While dealing with fragmented responses was fun and a definite improvement, it's not the kind of thing most people appreciate. People don't usually get excited over fixes, but they will get weak-kneed for new features - so I decided to add container/bucket management, too! Specifically, it's now possible to use BlobStoreApi
to list, create, and delete Azure containers and S3 buckets using the following asynchronous methods:
public abstract void GetContainers(Action<IEnumerable<string>> getContainersCompleted, Action<Exception> getContainersFailed); public abstract void CreateContainer(string containerName, Action createContainerCompleted, Action<Exception> createContainerFailed); public abstract void DeleteContainer(string containerName, Action deleteContainerCompleted, Action<Exception> deleteContainerFailed);
To be clear, each instance of AzureBlobStoreClient
and S3BlobStoreClient
is still associated with a specific container (the one that was passed to its constructor) and its blob manipulation methods act only on that container - but now it's able to manipulate other containers, too.
Aside: In an alternate universe, I might have made the new container methods static because they don't have a notion of a "current" container the same way the blob methods do. However, what I want more than that is to be able to leverage the existing infrastructure I already have in place for creating requests, authorizing them, handling responses, etc. - all of which take advantage of instance methods onRestBlobStoreClient
thatAzureBlobStoreClient
andS3BlobStoreClient
override as necessary. In this world, it's not possible to have static methods that are also virtual, so I sacrificed the desire for the former in order to achieve the efficiencies of the latter. Maybe one day I'll refactor things so it's possible to have the best of both worlds - but for now I recommend specifying anull
container name when creating instances ofAzureBlobStoreClient
andS3BlobStoreClient
when you want to ensure they're used only for container-specific operations (and not for blobs, too).
Other improvements
With containers getting a fair amount of love this release, it seemed appropriate to add a containerName
parameter to the AzureBlobStoreClient
constructor so it would look the same as S3BlobStoreClient
's constructor. This minor breaking change means it's now necessary to specify a container name when creating instances of AzureBlobStoreClient
. If you want to preserve the behavior of existing code (and don't want to have to remember that "$root" is the magic root container name for Azure (and previous default)), you can pass AzureBlobStoreClient.RootContainerName
for the container name.
In the process of doing some testing of the BlobStoreApi
code, I realized I hadn't previously exposed a way for an application to find out about asynchronous method failures. While it's probably true that part of the reason someone uses Azure or S3 is so they don't need to worry about failures, things can always go wrong and sometimes you really need to know when they do. So in addition to each asynchronous method taking a parameter for the Action to run upon successful completion, they now also take an Action<Exception>
parameter which they'll call (instead) when a failure occurs. This new parameter is necessary, so please provide a meaningful handler instead of passing null
and assuming things will always succeed. :)
I've also added a set of new #defines to allow BlobStoreApi
users to easily remove unwanted functionality. Because they're either self-explanatory or simple, I'm not going to go into more detail here (please have a look at the code if you're interested): BLOBSTOREAPI_PUBLIC
, BLOBSTOREAPI_NO_URIS
, BLOBSTOREAPI_NO_AZUREBLOBSTORECLIENT
, BLOBSTOREAPI_NO_S3BLOBSTORECLIENT
, and BLOBSTOREAPI_NO_ISOLATEDSTORAGEBLOBSTORECLIENT
.
Aside: Actually,BLOBSTOREAPI_PUBLIC
deserves a brief note: with this release of theBlobStoreApi
, the classes it implements are no longer public by default (they're expected to be consumed, not exposed). This represents a minor breaking change, but the trivial fix is to#define BLOBSTOREAPI_PUBLIC
which restores things to being public as they were before. That said, it might be worth taking this opportunity to make the corresponding changes (if any) to your application - but that's entirely up to you and your schedule.
The last thing worth mentioning is that I've tweaked BlobStoreApi
to handle mixed-case blob/container names properly. Formerly, passing in upper-case characters for a blob name could result in failures for both Azure and S3; with this change in place, that scenario should work correctly.
New BlobStore and BlobStoreOnWindowsPhone samples
I've updated the combined BlobStore
/BlobStoreOnWindowsPhone
download to include the latest version of BlobStoreApi
with all the changes outlined above. Although there are no new features in either sample, they both benefit from fragmented container support and demonstrate how to use the updated API methods properly.
New Delay.Web.Helpers release and updated sample
The Delay.Web.Helpers
download includes the latest version of BlobStoreApi
(so it handles fragmented containers) and includes new support for container management in the form of the following methods on the AmazonS3Storage
class:
/// <summary> /// Lists the blob containers for an account. /// </summary> /// <returns>List of container names.</returns> public static IList<string> ListBlobContainers() { ... } /// <summary> /// Creates a blob container. /// </summary> /// <param name="containerName">Container name.</param> public static void CreateBlobContainer(string containerName) { ... } /// <summary> /// Deletes an empty blob container. /// </summary> /// <param name="containerName">Container name.</param> public static void DeleteBlobContainer(string containerName) { ... }
As I discuss in the introductory post for Delay.Web.Helpers, I'm deliberately matching the existing WindowsAzureStorage API with my implementation of AmazonS3Storage
, so these new methods look and function exactly the same for both web services. As part of this release, I've also updated the Delay.Web.Helpers
sample page to show off the new container support as well as added some more automated tests to verify it.
Summary
While I hadn't thought to get back to blobs quite so soon, finding out about the fragmented response issue kind of forced my hand. But all's well that ends well - I'm glad to have added container supported to Delay.Web.Helpers
because that means it now has complete blob-related feature parity with the WindowsAzureStorage
web helper and that seems like a Good Thing. What's more, I got confirmation that the Windows Azure Managed Library does not support Silverlight, so my BlobStoreApi
is serving duty as an unofficial substitute for now. [Which is cool! And a little scary... :) ]
Whether you're using Silverlight on the desktop, writing a Windows Phone 7 application, or developing a web site with ASP.NET using MVC, Razor, or WebMatrix, I hope today's update helps your application offer just a little more goodness to your users!