The ability to have a private AppFabric cache deployed as a cluster of worker roles, or piggy-backing your existing roles, is great. But there are some things to watch out for, which can cost you a lot of time, headache, and outage if it finds it’s way to production.
For those of you new to AppFabric caching, there are some great “getting started” tutorials and info here. If you are currently using the Azure Shared Caching Service, details on how to migrate are here. At the end of the day, the only difference is a little bit of configuration, and different endpoints (no longer using a shared service, but your own roles). The AppFabric library is the same for either mode.
Caching frequently accessed, but seldom updated data can give your application a huge performance boost. And by using dedicated instances, and not the AppFabric shared cache, there is virtually no limit to the size of your cache or how many times you can access it. On the surface, it’s a very easy to implement feature with a big gain! However, without digging a lot deeper, you can find yourself in a world of hurt.
Looking for an elegant solution to out-of-process Session State handling that is faster (and cheaper) than SQL? Caching could be your new best friend.
At Stackify, we’ve implemented AppFabric caching for certain aspects of our application, and it dropped execution time on web requests to a fraction of what they were before.
What to Watch Out For
Establishing your cache cluster is easy and there’s really nothing to do. The walkthroughs above are all you need. Configuring your client is a bit different. The documentation shows a simple config and implementation:
<autoDiscover isEnabled="true" identifier="[cache cluster role name]" />
<!--<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />-->
<crashDump dumpLevel="Off" dumpStorageQuotaInMB="100" />
Then create your client code:
// Cache client configured by settings in application configuration file.
DataCacheFactory cacheFactory = new DataCacheFactory();
DataCache cache = cacheFactory.GetDefaultCache();
// Or DataCache cache = cacheFactory.GetCache("MyCache");
// cache can now be used to add and retrieve items.
// Add the string "value" to the cache, keyed by "item"
That’s it! You’re well on your way to huge performance gains in your application. You’ve saved the day and you’re the hero of the dev team, and your Ops manager who is tired of your app crashing every time you get busy! Yay….wait… what? We’re not done yet? Not even close.
“Surely you must be kidding?” you ask me. “Even Hanselman makes it look easy.”
Ok, I admit, that link is old, but what I’m about to share was as true then as it is now. Follow these 2 simple rules and you’ll be on your way to caching bliss.
Thou shalt implement a singleton pattern!
If you begin searching deep through the bowels of the MSDN documentation on Caching, you’ll find that it’s recommended to only have one instance of your DataCache (or DataCacheFactory) in each application instance. Establishing a connection to the cache cluster has quite a bit of overhead. Just open up the DataCacheFactory class in reflector and see what it’s doing; first checking the Azure Service Runtime for all running roles in the deployment and looking for any cache roles that match the name given in your web.config. It then establishes a connection to it over TCP. There’s no reason to do that each and every time. (This happens in Microsoft.ApplicationServer.Caching.AzureClientHelper)
The second reason you want to use a singleton model is due to the way in which the DataCache pools connections, which brings us to the next rule.
Thou shalt configure connection pooling!
AppFabric Caching uses a very specific connection model and everything you need to know is right here. I won’t repeat all of the details, except for this one very, very important piece that is easy to overlook.
When you use Windows Azure Caching hosted on Windows Azure roles, connection pooling is not always the default even from the application configuration file. This is a known issue with role-based Caching. To enable connection pooling with role-based Caching, you must explicitly setuseLegacyProtocol to
false. The following configuration section shows this setting being used where the role that hosts Caching is named
Understand the details of the connection pooling, and then make sure you set useLegacyProtocol to false, so your cache factory is actually using the pooling. It will save you a lot of time and headache.
Without following the two rules above, you’re going to run into a lot of concurrency problems. We’re talking “one user on your app can crash it” problems. AppFabric caching is great, but can be deadly when used without fully understanding how it works.