Chapter 12. Data caching

This chapter covers

  • System.Runtime.Caching
  • What should I cache?
  • Notes on distributed caching

In the same way that you might keep milk in your fridge until it reaches its expiration date, as I said in chapter 4, browsers can cache information about a website for a set duration of time. When your milk is old, you buy new. After data expires, a browser will fetch the updated version. Much like caching data on a user’s browser with HTTP caching, as discussed in chapter 4, you can do the same thing with frequently used information on the server. By caching data on the server, you can easily store information retrieved from the database, or perhaps even an expensive method.

Data caching is the process of storing frequently used data on the server to fulfill subsequent requests. If some data doesn’t change often, store it on the server so you don’t have to make an expensive call to the database the next time you request it.

Data caching is one of my favorite server-side techniques for speeding up an application and making it more robust. The .NET Framework has great built-in support for data caching and it can be applied to a web application in no time at all. This chapter runs through setting up data caching in a web application and shows how easy it is to use this great feature.

12.1. Server-side data caching

This may be the final chapter in this book, but it covers one of the most important aspects of the performance of a web application: server-side data caching. Most applications on the web will work with frequently used data that isn’t updated all that often. This makes it a prime candidate for data caching. For example, you may retrieve a list of country names from the database that doesn’t change often, or you may retrieve a list of default settings that only changes once every six months. These chunks of data are perfect candidates for data caching, and you can speed up your application considerably by adding them into a cache.

Figure 12.1 shows caching can be applied to layers in the application. If used effectively in each layer in your application, caching can make your application extremely fast.

Figure 12.1. Caching can be applied to different layers of your application.

Fortunately, caching doesn’t extend only to databases. You could cache the result of a call to a web service or even a long-running code calculation. If code is called frequently but the returned result doesn’t change often, it’s ideal for caching. Because the data is retrieved from memory, it’s often returned instantly.

12.2. System.Runtime.Caching

The .NET Framework has a built-in set of classes that fall under the System.Runtime.Caching namespace. This namespace contains types that let you implement an in-memory cache in your .NET Framework applications. The cache object enables you to store everything from simple name/value pairs to more complex objects, such as datasets and entire web pages. The classes are also extensible and if you’re looking for more flexibility, they allow you to create custom caching providers. The built-in classes that fall under the System.Runtime.Caching namespace are easy to implement and can produce impressive results.

You’ve been working with the Surf Store application throughout this book. Now you will learn how easy it is to add caching to any ASP.NET application and improve its web page load times. To get started, you need to add a reference to the System.Runtime.Caching library in your project.

In figure 12.2, a reference to the System.Runtime.Caching namespace is added to start caching in the sample application. Because you’re going to add caching in the sample application’s data layer, I’ve added the reference to the SurfStoreApp.Data project.

Figure 12.2. A reference to System.Runtime.Caching added to your application

A few simple methods like the ones shown in the next listing are enough to get started caching.

Listing 12.1. Basic methods to add and retrieve items from cache

The first line initializes a new instance of the MemoryCache class, and you reference to the default MemoryCache instance. The MemoryCache object is flexible and allows multiple instances inside a single application.

A method allows you to add an item into cache with a key, which you use to retrieve the object from cache when required. It’s worth mentioning that MemoryCache is not as strict as dictionary-based collections; if you request an item key that doesn’t exist, you’ll get a null rather than a runtime error.

When you’re building your application, you might come across a scenario in which similar objects have similar names. If that happens, consider building a dynamic key string to identify your objects. If you try to add an object into the cache and a key with the same name already exists, the new object will overwrite the older one.

The code in the previous listing is effective, but it could be rewritten so it’s easier to reuse and so it takes a dynamic key into account. The following listing is a more effective data caching class you can use throughout your application.

Listing 12.2. Adding and retrieving items from cache

This code can be used throughout your application to cache data and retrieve different object types from cache. It contains a set of useful dynamic methods and should cover the basic caching needs of an application.

12.3. What should I cache?

You can easily store the results of any expensive or long-running operations that aren’t likely to change in memory, so they can be retrieved at a later time. The kinds of data you would store in memory might include

  • Database lookups
  • Expensive calculations that are done in code
  • The data from any I/O read (XML file, text file, and so on)
  • The result of a web service call

In chapter 4 you investigated HTTP caching and the flexibility of IIS for storing data that doesn’t change often in the client’s browser. Remember, if data is cached it’s refreshed only after it expires. Bear this in mind when designing your application, because caching data that changes frequently can cause headaches when you’re debugging your application.

12.4. The sample application

In this chapter, you’re going to apply caching to the Surf Store application you’ve been optimizing throughout this book. The Products page of the sample application retrieves product information from the database to display on the Products page. This information doesn’t change often and is a prime candidate for data caching.

I’ve chosen to add caching in the project’s Logic layer. You could easily choose to add caching in the Data layer if the GetProductDetailByCategory method is reused frequently. By adding the caching on the Data layer, it will allow you to share the cached information with other methods in the application. The following listing shows the code in action.

Listing 12.3. Using the cache in the sample application

The code builds a dynamic key that caches the data and retrieves it at a later stage. Because the key is based on the passed-in category and the method name, it should be unique every time.

Look in the cache with the cache key to see if any data is associated to that key. If there’s nothing, retrieve the data from the database and add it into cache so it can be retrieved from cache next time.

If the key does find something in cache, return the cached data instead of going to the database. Retrieving the data from cache instead of making a call to the database is significantly faster because you’re retrieving directly from memory in the application. This technique can be applied throughout your application to any long-running operations that take place during execution.

12.5. Notes on distributed caching

If you run your application in a load-balanced environment with multiple servers, you may need to assess the way you cache your data. In this chapter’s example code, all the data in the cache will be stored within the application itself. If your application is running across multiple servers, you’d have a near duplicate copy of the cached data on each server. This can become a problem on your server if the memory grows quite large or if the cached data on each server is out of sync.

To avoid these pitfalls consider using distributed caching instead. Distributed caching allows the cache to span multiple servers so it can grow in size and in transactional capacity.

In figure 12.3, the left side shows how each web server has its own individual copy of the cache. The right side shows the distributed cache with a combined cache. Both servers retrieve data from the same cache instead of accessing their own individual cache. Your servers are more efficient when storing data this way because they’re sharing the combined cache. A distributed cache also provides high availability and scalability. If either of the web servers goes down, you’ll still have the cached data available for the other web servers.

Figure 12.3. Distributed caching across multiple web servers versus caching on individual web servers

Distributed caching options available today include

Most of these distributed caching solutions work in a similar way to the System.Runtime .Caching namespace. They allow you to store a chunk of data using a simple key/value pair that can easily be retrieved at a later stage.

If you’re looking to grow your application and make sure it’s scalable and performing efficiently, consider distributed caching as an alternative to the standard caching in the System.Runtime.Caching namespace.

12.6. Summary

In this chapter you learned about in-memory caching in the .NET Framework. The System.Runtime.Caching namespace provides a useful tool for caching frequently accessed data. Instead of continually executing expensive I/O operations, the data returned from these operations can be stored in a cache that allows you to instantly retrieve the data. This cache can be used for common operations such as database lookups and expensive calculations, or for accessing data from a simple I/O read. Using caching in your application will make sure it’s scalable as well as extremely fast.

You also briefly investigated implementing a distributed cache if you’re running your application across multiple servers. Distributed cache can be an effective way to make sure your application performs at its best if it spans multiple web servers.

12.7. Look at how far you’ve come!

You’ve completed the final chapter in this book. You’ve covered a lot of information about different web performance optimization techniques. It’s time to reflect on the improvements that you’ve made.

The simple changes you added to the sample application at the end of each chapter gave you better performance scores, but how much did they influence the speed of the site? In order to compare the load times of the application before and after making the optimizations, I uploaded the sample application to a web server and noticed some interesting results. If you would like to test the differences for yourself, please check out the following links:

Most of the tests I’ve been running until now have been against my local machine and didn’t simulate a real-world scenario. In order to show the differences between the two samples, I needed to upload them and test them against a real server. I ran both of these websites against the Webpagetest tool and selected a video to highlight differences in load time. The image in figure 12.4 was generated as a side-by-side comparison of the load times of these two sample applications. As you’ll notice, the image shows that 1 second into the page load, the optimized version is already showing content to the user. The unoptimized version hasn’t shown any content and didn’t do so until around 2 seconds in.

Figure 12.4. A comparison of the optimized sample application versus the unoptimized one. Note that after 1 second, the unoptimized result (top) hasn’t even started showing content to the user.

Figure 12.5 shows how the optimized version has completed loading at 3.5 seconds, but the unoptimized version isn’t close to loading all its content yet!

Figure 12.5. The optimized version finished loading within 3.5 seconds, but the unoptimized version (top) is still loading content.

The unoptimized version of the site takes around 7 seconds to load, while the optimized version completed in 3.5 seconds; this is a remarkable improvement. You can see some even more interesting statistics in table 12.1.

Table 12.1. A detailed comparison of the Surf Store application before any optimizations and after you’ve applied optimization techniques.

Before optimization

After optimization

HTTP Requests 24 13
Page Weight 875.72 KB 672.75 KB
Total Load Time 7 seconds 3.5 seconds

I encourage you to try this same test for yourself. The two versions of the website are freely available for you to compare. Fire up www.webpagetest.org and see the differences for yourself. The optimized version of the Surf Store application is a shining example of what a high performance website should be!

If you’ve been following along with each chapter, you’ll have learned how easy it is to transform the load times of your web pages. All the techniques you covered in this book are simple changes you can easily apply to your websites today. There is no better time to start improving your website’s speed than now!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.52.94