Deep understanding of caching mechanisms in Ruby on Rails _ruby topics

Source: Internet
Author: User
Tags memcached ruby on rails

Several scenes

First, let me take you through a few changingthepresent.org pages. I will show several places in the site that need to be cached. Then, we'll point out the choices we make for each of them and the code or strategy that we use to implement those pages. In particular, it will focus on the following topics:

    • Full static page
    • Completely dynamic page with almost no change
    • Dynamic page Fragment
    • Application Data

Take a look at the static page first. Almost every site will have a static page, as shown in Figure 1, which has our terms and conditions. You can browse the page by clicking the Register and then selecting whether to accept the user agreement. For changingthepresent, we removed all of the dynamic content from this page so that Apache could cache it. These will never be generated by the Rails server, according to the rules we have configured in Apache. So I don't have to think about the Rails cache at all.
Figure 1. User Agreement

Next, take a look at the full dynamic page. Theoretically, changingthepresent can have some dynamically built pages, but these pages generally rarely change. Since almost all pages will show whether the user is logged in, we are not paying much attention to the cache.

Again, look at the page fragment cache. The home page shown in Figure 2 is completely static, and now there are some elements that have become dynamic. Every day, the page displays a series of gifts, either randomly selected or selected by our administrator. Note Those gifts under the title "A Few of our Special Gifts for Mother's Day", and also note the link to the rightmost display as "login." This link depends on whether the user is logged on. We can't cache the entire page. Pages can only be changed once a day.
Figure 2. Home

Finally, consider the application. Unless you're surfing the web before 15, all the interesting sites you're experiencing are dynamic. Modern applications are mostly layered and can be made more efficient by adding caches between tiers. Changingthepresent uses some caching in the database layer. Next, I'll delve into the different types of caching and describe what caching we have for changingthepresent.
Caching Static Content

Mongrel is a WEB server that is written by Zed Shaw using 2500 lines of Ruby and C. This small server consumes very little memory and is ideal for Ruby Web applications such as Rails, Nitro, Iowa, and so on. Mongrel can run on UNIX? and Linux? , you can also run on the Win32. Mongrel can also often run as an agent on the back end of another WEB server (such as Apache or Litespeed), but this is not necessary-because Mongrel is an HTTP server that can be used in conjunction with all of your preferred HTTP tools.

In addition to the image, there is not much to say about caching the contents of static data. Since our site is a charitable portal, it means that we need to focus more on the user's feelings, such as adding more images or videos. But our Web server Mongrel does not serve static data very well, so we use Apache to service image content.

We are now moving towards using the graphics accelerator Panther Express to cache the most frequently used images so that they can be accessed by our customers faster. To take this strategy, we will need a subdomain images.changingThePresent.org. Panther Express provides image services directly in the image's local cache, and then sends the request to us. Since the Panther service does not know when we will change the image, we use the HTTP header to expire it, as follows:
HTTP Cache Invalidation Header

http/1.1 OK
cache-control:max-age=86400, must-revalidate
expires:tues, Apr 2007 11:43:51 GMT
Last-modified:mon, APR 2007 11:43:51 GMT

Note that these are not HTML headers. They are built independently of Web page content. The WEB server will be responsible for building these HTTP headers. A series of articles about Rails like this one is a bit off to detail the Web server configuration, so I'll go directly to the topic of cached content that can be controlled by the rails framework (see Resources for more on Web server configuration).
Page Caching

If dynamic pages do not change frequently, you can use page-level caching. For example, blogs and bulletin boards Use this kind of caching. With page caching, Rails can be used to build dynamic HTML pages and store this page in a public directory so that the application server can serve the dynamic page just like any other static page.

If the page is already cached, then there is no need to introduce rails, which is the fastest cache in rails. At the bottom, page caching is actually very easy to implement in Rails. Both the page and the segmented cache occur at the controller level. You need to tell Rails the following:

    • Which pages do you want to cache?
    • When the page content changes, how can you make the page expire in the cache?

You can enable page caching by using the Caches_page directive in the controller class. For example, to cache Privacy_policy and user_agreement pages in About_us_controller, you can enter the following code:
Listing 2. Enable page Caching

Class Aboutcontroller < Applicationcontroller
 caches_page:p rivacy_policy,: user_agreement 
End

The expiration of the page expires can be achieved by expire_page instructions. To make the page expiration expire when Rails invokes the New_pages action, you can use the following code:
Listing 3. Make page invalid

Class Aboutcontroller < Applicationcontroller
 caches_page:p rivacy_policy: user_agreement 
 
 def new_pages
  expire_page:action =>:p rivacy_policy
  expire_page:action =>: user_agreement
 End
 


In addition, there are a few minor issues to note, such as URLs. URLs cannot depend on URL parameters. For example, you should use GIFTS/WATER/1 rather than gifts/water?page=1. Using this type of URL in ROUTES.RB will be very easy. For example, there is always a tab parameter in our page to show which tab is currently selected. To use this tab as part of the URL, we will have the following routing rules:
Listing 4. Routing Rules for Tabs

Copy Code code as follows:
Map.connect ' Member/:id/:tab ',: Controller => ' profiles ',: Action => ' show '

The same approach is required for those lists that have page parameters and other pages that depend on the URL parameters. In addition, you need to consider security issues.

If the page is already in the cache, the Rails framework is not used and the server does not manage security for you. The WEB server will be more willing to render any page within the cache, regardless of whether the user has permission to view it. So, if you're concerned about who the page is, then don't use the page cache.

If you just want to cache a simple static page, it should be sufficient to understand the above content. As long as the content is simple, it is not difficult to achieve.

When you want to cache more complex content, you need to make trade-offs. Because the page you want to cache is highly dynamic, the expiration logic becomes more complex. To handle complex expiration failure logic, you will need to write and configure a custom cleaner (sweeper). When some controllers are fired, the classes delete the selected elements from the cache.

Most custom cleaner will look at some model objects and cause one or more cached pages to expire due to a change in the firing logic. Listing 5 shows a typical cache cleaner. In this cleaner, the developer can define an activity record event, such as After_save. When this event is fired, The cleaner will also be fired and the expiration of a specific page in the cache is due to expire. The expiration failure shown by this case is based on the Expire_page method. Most strict applications use Ruby's excellent file system utility to explicitly delete cached pages.
Listing 5. A typical observer

Class Causecontroller < Applicationcontroller
  cache_sweeper:cause_sweeper
...

Class Causesweeper < Actioncontroller::caching::sweeper
 observe cause
  
 def after_save (record)
  expire_ Page (: Controller => ' causes ',: Action => ' Show ', 
        : IDs => record.id)
  Cause.nonprofits.each do |nonprofit |
   Expire_page (: Controller => ' nonprofits ',: Action => ' Show ', 
         : ID => nonprofit.id)
   End
  End


Now, you may start to feel a bit of a drawback to the page cache: complexity. While you can do a good job of page-level caching, inherent complexity makes it difficult to test your application, which in turn increases the likelihood of bugs in your system. Also, if the page is different for each user, or if you want to cache a page that has been authenticated, you will need to use a way other than page caching. For Changingthepresent, we have to deal with two cases because we have to change the links on the basic layout based on whether the user is logged on or not. For most pages, we don't even consider using page-level caching. To give you a deeper understanding of page-level caching, in the Resources section of this article, I have specifically given a series of links to excellent articles on page-level caching. Next, delve into another form of full-page caching, the action cache, in the future.
Action Caching

At this point, you've learned the main advantages and main drawbacks of page caching: For most page searches, you don't need to consider Rails at all. The advantage of page caching is that it is fast. The disadvantage is lack of flexibility. If you want to cache an entire page based on conditions within the application-for example, identity authentication-you can use the action cache.

The action cache works the same way as the page cache, but there is a slight difference in the process. Rails actually invokes the controller before rendering the action. If the page rendered by the action already exists in the cache, then Rails renders the page in the cache instead of rendering it again. Because Rails is now used, the speed of the action cache is slower than the page cache, but the advantages are obvious. Almost all Rails authentication modes use the before filter on the controller. The action cache allows you to take advantage of any filters on the authentication and controller.

From the perspective of statement composition, the action cache and page caching should be very similar, only the instructions are not the same. Listing 6 shows how to use the caches_action directive.
Listing 6. Enable action Caching

  Class Aboutcontroller < Applicationcontroller
   Caches_action:secret_page,: secret_list 
  End

Cache expiration failures and how the cleaner works should be the same. The reason we don't use the action cache is the same as the reason we don't use the page cache, but the segmented cache is more important to us.
Caching page Segments

With partial caching, you can cache part of the page, and the cached content is often layout-like. To use a segmented cache, developers need to determine the fragmentation by enclosing a piece of content directly on a Web page, as shown in Listing 7. On the changingthepresent.org, we use the segmented cache to cache the home page and several other pages. All of these pages use database intensive access and are mostly our most popular pages.
Listing 7. Determining Cache Fragmentation

<% cache ' Gifts_index ' do%>
   
 

In Listing 7, the cache helper identifies the partition to be cached. The first parameter is the unique name that identifies this cache partition. The second parameter contains the code block-the code between the first do and the last end-this code block accurately determines the RHTML partition to cache.

Our site has only one homepage, so it's very easy to name this page. Elsewhere, we use a specific method to determine the URL of this page to uniquely identify the cache fragment. For example, when we cache code for specific content, such as world peace or poverty reduction, we need to use the code in Listing 8. The code will look for a permanent URL, also known as permalink.
Listing 8. To identify a cache fragment by URL

<% cache @cause. Permalink (Params[:id]) do%>

Typically, when you cache a separate page, you need to use the cleaner to expire it. Sometimes, it's easier and simpler to use simple, time-based object expiration. By default, Rails does not provide such a mechanism, but there is a plug-in named Timed_fragment_cache to do this. With this plug-in, I can specify a time-out, either in the cached content or in the controller code that provides dynamic data for this page. For example, the code shown in Listing 9 allows you to build dynamic data for this page. The When_fragment_expired method executes only if the associated cache fragment expires. This method takes a parameter that specifies the time length of the timeout, and it also accepts a block of code that specifies which content needs to be rebuilt when content expires. I can also choose to specify timeouts and caching methods in the Rhtml page, but I prefer to use a controller based approach.
Listing 9. Time-based cache expiration

def index
 when_fragment_expired ' causes_list ', 15.minutes.from_now do 
  @causes = cause.find_all_ordered
 End End


If you can tolerate data being slightly stale, using timed expiration mechanisms can greatly simplify caching policies. For each cached element, you simply specify what you want to cache, any controller actions that generate dynamic content, and timeouts. Similar to the page cache, you can use the Expire_fragment:controller => controller, if necessary, by using the action => action: The ID => ID method explicitly makes the content expire. This method works the same way that the cached action and the cached page expiration are invalidated. Next, I'll describe how to configure the backend.
Memcached

So far, I've covered the page and fragment caching model for Ruby on Rails. Once you've seen the API, you can now define where the cached data will be. By default, Rails puts cached pages into the file system. Cached pages and actions go into the public directory. You can configure the storage location of the cached fragment. To do this, you need to use memory storage, a file system (in a defined directory), a database, or a service called memcached. For changingthepresent.org, we use memcached.

Memcached can be imagined as a large hash chart, which is available over the network. Memory-based caching is faster, and network-based caching is more scalable. With plug-in support, Rails can use memcached to cache segments and ActiveRecord models. To use it, you need to install memcached (see Resources for more information) and configure it in ENVIRONMENT.RB (or other environment profiles, such as PRODUCTION.RB).
Listing 10. Configuring caching

Config.action_controller.perform_caching = True

memcache_options = {
 : c_threshold => 10_000,
 : Compression => false,
 :d Ebug => false,
 : readonly => false,
 : UrlEncode => false,
 : TTL =>
 : Namespace => ' Igprod ',
 :d isabled => false
}

CACHE = Memcache.new memcache_options

Listing 10 shows a typical configuration in which the first line config.action_controller.perform_caching = True enables caching. The next line prepares the caching options. Note that many of the options are available to allow you to get more debug data, disable caching, and define the cached namespace. The memcached site, given in the Resources section, can find more information about configuration options.
Model Cache

The last cache we use is based on the model cache. We are using a custom version of the cache plug-in called Cachedmodel. Model caching is actually a limited form of database caching. Caching is easy to enable by model.

For a model to use a caching solution, simply extend the Cachedmodel class, not the extended ActiveRecord, as shown in Listing 11. Cachedmodel extended activerecord::base. ActiveRecord is not a whole-object relational mapping layer. This framework relies heavily on SQL to perform complex features, and users can easily descend to SQL if needed. Using SQL directly can cause caching problems because the caching layer must handle the full result set rather than a single database row. Processing a complete set of results is often problematic, and it is almost impossible without deep logic to support the application. For this reason, the focus of the Cachedmodel is placed on the cache of a single model object and only the query that returns a single row of results is accelerated.
Listing 11. Using Cachedmodel

Class Cause < Cachedmodel

Most Rails applications will repeatedly access multiple entries, such as user objects. The model slows down in many cases and can obviously speed up. For Changingthepresent, we are just beginning to speed up model-based caching.
Concluding remarks

Although Ruby is a highly productive language, the explanatory nature of the language makes it less desirable for performance reasons. Most of the major Rails applications will compensate for some deficiencies by effectively leveraging the cache. For changingthepresent.org, we mainly use segmented caching and use a time based method through the controller to invalidate the cache fragment expiration. This works well for our site, even though some of the pages will change based on the users who are logged in.

We also studied the effects of using Cachedmodel classes supported by memcached. While our research is limited to the impact of caching on database performance, early results are promising. In the next article, I'll introduce some practical tips that you can use to optimize your database for another real-world example of Rails.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.