How to best leverage Windows Azure AppFabric Caching Service in a Web role to avoid most common issues
Having spent the last day and a half in air-travelling “stand-by hell”, made me realize how much a simple well-constructed explanation of what is taking place can help you better deal with things a lot better. The following example leverages the caching service in a web role, this can help you better learn the intricacies of a custom approach that lets you cache your own .NET serializable objects. The bases on which should help minimized your time spent, in some type of “coding-stand-by hell”.
Currently, a simple search on Azure web role can give you lots of information on how to create one, and in the same manner you can get information on how to leverage the power of Azure AppFabric Cache Service in your web role to cache session state (this requires a configuration update). However, there is little information for those who want to use a customized approach and cache their own .NET serializable objects in a web role. At first, this does not seem to be much of a problem, since merely using the Azure AppFabric Cache APIs in your web role should suffice. Well, that is not exactly the whole history and hence, in this blog, I will explain the things that must be taken into consideration to minimize any possible frustration, increase performance and use the correct AppFabric Cache connection quota.
The method
Since I have created a blog that shows the concepts of the APIs used in AppFabric Cache, I went ahead and leveraged the same sample code to migrate the whole solution into a web role. Since both cache technologies (on-prem and in the cloud) are very similar and have kept API parity (some exceptions may exist, but that will be a topic in a different blog) then it follows that the transition from one to the other should also be simple, which is for the most part true. However, we are not just going from on-prem to cloud, but also from Windows forms to an ASP.Net page – we have two “bridges” to cross.
For those readers that are very familiar with the context of the HTTPHandler, you maybe ok to skip to the next section, titled: “Plugging the AppFabric Cache Code”.
For reference on the difference between the on-prem and cloud technologies see this MSDN article.
The Interface
With these plans in mind, I went ahead and created a web role in Visual studio 2010 using the latest SDK and then removed all the default content (the one showing “Welcome to ASP.NET”) from the ASP.Net page. Then, I literally copied, via the traditional method of holding the Ctrl key while selecting, via mouse clicks, all the controls in the windows form from the old project (Form1.cs [Design] page). Then I released the keys and clicked “Ctrl + c”, and then pasted (Ctrl + v) all of the controls into the new web role project (Default.aspx file). Then I hit F5 and it worked! This meant that all my controls were going to have the same name. This only copied the interface and not the code behind. Below I show the old and new interface.
Figure 1: The windows form
Figure 2: The ASP.Net form
To break down my steps, instead of copying and pasting “all” of the code sections (in the windows form it is the Form1.cs file, and in the ASP.NET/web role it is the default.aspx.cs file), I only copied the code that updates the tStatus WebControls.TextBox and throw my own test exceptions (i.e. no cache code is used instead instrumented exceptions were thrown). At this point, I tested the interface, and received the following exceptions.
Figure 3: Dangerous request error
You will notice that the first action (say “Add”) will correctly update the text box with the instrumented exception, but the 2nd action will think that you are trying to do a post back to the server with the remaining information (the instrumented exception) from the status box and this is seen as a type of dangerous request. As per the message shown in the exception details above, one option is to change the request validation mode, but since we are not really trying to send any exception information to the server, the recommended approach is to disable the state of the text box to not perform a post back to the server. This can be achieved by changing the text box property from Enabled=True to Enabled=false, as shown in Figure 4.
Figure 4: Change to the TextBox property
Note that I have to do this since, for didactical purposes, I show the raw exception on the web UI but for production purposes, the exception should be handled in a separate class (anywhere but in the web UI), and probably put into a tracelog and in parallel throw a more friendly message, which will be shown to the Web UI on a stateless control (as per the property change mentioned above). Similar schemes can be created for value checking, such null return values from DataCacheItemVersion, all this with the goal of handling errors outside default.aspx.cs.
Another important thing to notice, which will become even more apparent later, is that the code within default.aspx.cs runs in the context of HttpHandler, which ASP.NET uses to map each HTTP request (one for every request in essence every web user). This means that every single line of code written in this context will be duplicated for every HTTP request. Hence, keep the amount of code in this context to a minimum (more code = more instances of that code repeatedly running), which will include but will not be limited to error tracing and validation. Notice, however, that I am not doing this in this sample for the purpose of simplicity and clarity.
Plugging the AppFabric Cache Code
The information on how to create and connect to a Windows Azure AppFabric Caching service can be found in a few blogs, here is one from MSDN. In essence, to use the AppFabric cache cloud Service, one takes the old code and modifies the way the DataCacheServerEndpoint is instantiated (use the cache end point name instead of the DNS name of a node or nodes) and then leverage the given ACS token to authenticate against the service, as shown below in Figure 5. Note that the same can be done using the Application Configuration file, for more on it, see this MSDN article.
Figure 5: Cloud (code on the left) vs. On-Prem (code on the right)
Now paste all the code, as is, from the old windows form project and the web role will serve a web forms that works, but only for the first one or two simple actions (e.g., adding, removing, etc…). Using local cache or version handles will throw exceptions of missing or uninitialized objects.
The issue is that the default.aspx.cs code is instantiated for each HTTP request, and hence the DataCacheFactory , the DataCacheItemVersion and DataCacheLockHandle objects are all instantiated many times on every request (on every HttpHandler). If by chance you somehow get the same instance that handled your previous request then things may work but we need to make the solution more predictable. In the case of simple operations like Add() the DataCacheFactory is only needed once so those may work. It worked great on the windows form because it maintains state (i.e. the same object instanced was the one used) but due to the nature of HttpHandler this state needs to be handle in a different manner.
The easy solution would be to simply label all of these objects to be private static, which will not keep only one instance of this object so in the case of the DataCacheFactory, it will minimize the amount of connections created against the Windows Azure AppFabric Caching service. As mentioned in my blog on the release of the product, this has direct influence on your maximum amount of connections quota. If you create many instances of the DataCacheFactory object, you will quickly fill your connections quota, which in turn will render the service unusable for the next hour (as per quota behavior), at which point the service will go back online until the connection quotas are quickly drained again.
Why did not we care so much about this when we were on-prem? The fact was that the same thing was taking place, but as long as the DataCacheFactory was not Disposed after every use (or in any frequency), then the effect may not have been noticed. But, the fewer cache factories you need to recreate the less performance penalty you will pay. AppFabric cache (in both on-prem and cloud) leverage WCF for its network traffic, which means that each time a cache factory is recreated, a new WCF channel is created and this can be expensive in terms of performance. I will show this in practice in my next blog.
As previously stated, the best performance will be reached by keeping as little code as possible under the default.aspx.cs file. The solution that you will find in the final project (which you can download from this link) is to encapsulate all of the AppFabric cache objects created, API usage and some validation, into a separate class, which in the sample is named the AFCache class. To keep things further “locked”, the AFCache Class is encapsulated into a static class of its own. In effect, it is made into a singleton. This class can be seen in Figure 5 above, which also shows how the code only differs a little bit in the constructor. Notice that for simplicity and encapsulation, at this point there are no static assignments. You can also see how to initialize a DataCacheServerEndpoint and how the ACS token is handled.
Another very important distinction is the added configuration line to assign MaxConnectionsToServer. By default, this is only one. If we had two, then AppFabric Cache client will allow itself to create up to two WCF channels, if needed. Hence, if you find that performance with one channel is not enough then increasing this value may be an option, but connection quotas should be always kept in mind, more so if several Web roles are used. Each webrole will need at least one cache factory, and as it is advised for redundancy. The minimum solution requires at least two web roles; hence you will need at least two connections. In this minimum case, I will recommend reserving at least 3 available connections since a drop in connection within one hour, may be assumed as a third connection.
The code below, shows the static encapsulation of the AFCache class
//Encapsulating the AFCache class to be used as a singleton public static class MyDataCache { private static AFCache _SingleCache = null; static MyDataCache() { _SingleCache = new AFCache(); } public static AFCache CacheFactory { get{ return _SingleCache; } } }
Another important point is the encapsulation of AppFabric APIs within the AFCache class itself, here is how it is done in the sample, noticed how the highlighted areas indicate the action AppFabric Cache API.
public class AFCache { //...<Initializing cache factories and other private members>... public AFCache() { //...<Constructor, checking that cache factory is still alive>... } public DataCacheItemVersion Add(string myKey, string myObjectForCaching) { if (CheckOnFactoryInstance()) { return myDefaultCache.Add(myKey, myObjectForCaching); } return null; } public string Get(string myKey, out DataCacheItemVersion myDataCacheItemVersion) { if (!this.UseLocalCache && this.CheckOnFactoryInstance()) { return (string)myDefaultCache.Get(myKey, out myDataCacheItemVersion); } //Calling CheckOnLocalCacheFactoryInstance() will setup the local cache enable factory else if (this.UseLocalCache && this.CheckOnLocalCacheFactoryInstance()) { return (string)myDefaultCache.Get(myKey, out myDataCacheItemVersion); } else { myDataCacheItemVersion = null; } return null; } public DataCacheItemVersion Put(string myKey, string myObj) {...} public bool Remove(string myKey) {...} ... }
Here is a sample of how the get method, which is wrapped within the cache AFCache class, is called from default.aspx.cs. This is triggered when the Get_Click()method is fired from the web form by the user
RetrievedStrFromCache = (string)MyDataCache.CacheFactory.Get(myKey, out myVersionBeforeChange)
In Conclusion
Although parity does exist between the two versions of the technology (on-Prem and Cloud), there are small considerations that need to be seriously taken into account as part of the architecture. As a friend at work says, “we are making accountants out of coders”. That may be a little exaggerated, but it does illustrate the point that usability of the service is a concept that needs consideration. When working with cloud services, quotas have to be taken into account, which in turn makes the application take a more holistic, and even smarter approach to the usage of available services. This in turn, as in the case shown above, will also streamline execution – all and all, a good idea.
Author:
Jaime Alva Bravo
Reviewers:
Keith Bauer, Curt Peterson, James Podgorski, Olga Liakhovich
3 Comments
Leave a Reply
You must be logged in to post a comment.














Hi,
If the main overhead (connection establishedment & connection consumption) is with the creation of the DataCacheFactory, is the DataCache lifecycle managed in the same way just for convenience?
Bryn
The DataCache is your client – in a way it is your “handle to the factory”, it does not need to be dedicated to one DCF, as my sample shows it, it can be assign to different factories (the one been used). In the sample, I assign it to a factory with local cache turned off and then to one with local cache turned on. So its “lifecycle” does not need to be handle in the same way (but if it is convenient for the way you wrote the code then sure).
Pingback: How to use a WCF custom channel to implement client-side caching in a Windows Azure-hosted application | AppFabric CAT