3. Performance disadvantages of a chatty interface
In
the previous sections, we’ve discussed how you could store static data,
such as shirt sizes and materials, in SQL Azure or the Table service.
We’ll now look at the call sequences you’d need to make to render the
web page (shown previously in figure 1) and discuss the pros and cons of each approach.
Synchronous Calls
To retrieve all the data required to display the product details page shown in figure 1, you’d need to make at least four calls to the storage provider:
Retrieve the product details
Retrieve the list of size types
Retrieve the list of materials
Retrieve the list of personage types (men, ladies, and so on)
When developing an
ASP.NET web page, you should consider making asynchronous calls to
improve performance, but most developers will typically write
synchronous calls to retrieve the data. Figure 3 shows the synchronous call sequence for the product details web page.
As you can see, the
synchronous nature of the page means you have to wait until you receive
data back from the data provider (SQL Azure or the Table service) before
you can process the next section of the page. Due to the blocking
nature of synchronous calls and the latency involved in cross-server
communication, the rendering of this page will be much slower than it
needs to be.
Asynchronous Calls
Because size types,
materials, and personage types are sets of data that are both
independent of each other and independent of the returned product
details, you could use asynchronous calls instead. Retrieving the data
from the Table service asynchronously means you don’t have to wait for
one set of data to be returned before retrieving the next set.
Figure 4 shows the same call sequence as in figure 3, but this time using asynchronous calls.
As you can see in figure 4,
you no longer have to wait for data to be returned before you process
the next statement. Once all the data is returned, you can render the
page.
Tip
Here
we’ve used static data calls as our example. You can, however, use
asynchronous calls whenever you don’t have any relationship between sets
of data rendered on a page.
Now that you understand
how you can store and retrieve static data, let’s take a look at how you
can improve performance (and reduce the hosting bill) by using cached
data instead.
4. Caching static data
Regardless of your
chosen storage platform (SQL Azure or the Table service), you should
consider caching static data rather than continually retrieving the same
data from a table. This will bring large performance and cost benefits.
Because static reference data
hardly ever changes and is usually a pretty small set of data, the
in-process memory cache (such as the default ASP.NET cache) is a
suitable caching choice. You can always use a cache dependency to keep
the cache and the underlying backing store synchronized.
Let’s now take a look at how you can use the cache in the Hawaiian Shirt Shop website.
Populating the Cache
For frequently accessed
static data, you should probably populate the web role cache when the
application starts up. The following code, placed in the Global.asax
file, will do this:
protected void Application_Start(object sender, EventArgs e)
{
var sizeTypeContext = new SizeTypeContext();
HttpRuntime.Cache["sizeTypes"] = sizeTypeContext.SizeTypeTable;
}
In this example we’ve
populated the cache with data from the Table service, but the technique
is the same for using SQL Azure. We’re using a cache because we’re
working with static data, and we don’t want to hit the data source too
often. You might want to consider populating the caching when your role
instance starts, instead of when the ASP.NET application starts.
Populating the Drop-Down Lists
Now that you have your data in the cache, let’s take a look at how you can populate the drop-down lists with that data:
sizeDropDown.DataSource = (IEnumerable<SizeType>)Cache["sizeTypes" ];
sizeDropDown.DataTextField = "RowKey";
sizeDropDown.DataValueField = "SizeCode";
sizeDropDown.DataBind();
As you can see from this
code, you no longer need to return to the data store to populate the
drop-down list—you can use the cache directly.
Because
the cache will be scavenged when memory is scarce, you can give static
data higher priority than other cache items by using the cache priority
mechanism (meaning that other items will be scavenged first):
var sizeTypeContext = new SizeTypeContext();
HttpContext.Current.Cache.Insert("sizeTypes", sizeTypeContext.SizeTypeTable, null, new DateTime(2019, 1, 1), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
In the preceding code, the SizeTypes list will be stored in the cache with a High
priority, and it will have an absolute expiration date of January 1,
2019. If the web role is restarted, the cache will be flushed, but if
the process remains running, the data should remain in memory until that
date.
If the static data might
change in the future, you can set a cache dependency to keep the cache
synchronized or manually restart the role when updating the data.
Protecting Your Code from an Empty Cache
Because cache data is
volatile, you might wish to prevent the cached data from being flushed
by checking that the data is cached prior to populating the drop-down
list:
private IEnumerable<SizeType> GetSizeTypes()
{
if (Cache["sizeTypes"] == null)
{
var sizeTypeContext = new SizeTypeContext();
Cache["sizeTypes"] = sizeTypeContext.SizeTypeTable;
}
return (IEnumberable<SizeType>)Cache["sizeTypes"];
}
sizeDropDown.DataSource = GetSizeTypes();
sizeDropDown.DataTextField = "RowKey";
sizeDropDown.DataValueField = "SizeCode";
sizeDropDown.DataBind();
In this code, before the
drop-down list is populated, a check is run to make sure that the data
is already stored in the cache. If the data isn’t held in cache, it
repopulates that cache item.
By effectively caching
static data, you can both reduce your hosting bill and improve the
performance of your application. By using an in-memory cache for static
data on the product details page, you now have one data storage call per
application start up rather than four.
Using in-memory cache for
static data also means that your presentation layer no longer needs to
consider where the underlying data is stored.
Please be aware that the
examples in this section aren’t production-level code and have been
simplified to illustrate the concepts. If you’re implementing such a
solution, you should also take the following guidelines into
consideration:
Abstract your caching code into a separate caching layer
Don’t use magic strings (such as Cache["sizeTypes"])—use constants instead
Use cache dependencies
Prioritize your cache properly
Check that your cache is populated prior to returning data
Handle exceptions effectively