Java Cache Tutorial with Cache Dependency Injection (CDI)

From Resin 4.0 Wiki

(Difference between revisions)
Jump to: navigation, search
Line 48: Line 48:
 
   
 
   
 
     return cachedValue;
 
     return cachedValue;
 +
  }
 +
 +
  public String doLongCalculation(String key)
 +
  {
 +
    // database, REST, XML-parsing, etc.
 
   }
 
   }
 
  }
 
  }
  
 
Notice that all the code uses standard APIs without any need to reference Resin classes. Since only the configuration needs to select the Resin cache, your application can easily switch cache implementations.
 
Notice that all the code uses standard APIs without any need to reference Resin classes. Since only the configuration needs to select the Resin cache, your application can easily switch cache implementations.
 +
 +
== Cache Performance example ==
 +
 +
Since reducing database load is a typical cache benefit, it's useful to create a micro-benchmark to see how a cache can help. This is just a simple
 +
test with mysql running on the same server and a trivial query. In other words, it's not trying to exaggerate the value of the cache, because almost any
 +
real cache use will have a longer "doLongCalculation" than this simple example, and therefore the cache will benefit even more.
 +
 +
The micro-benchmark has a simple jdbc query in the "doLongCalculation" method
 +
 +
"SELECT value FROM test WHERE id=?"
 +
 +
and then to get useful data, the call to "doStuff" is repeated 300k times and compared with the direct call to "doLongCalculation" 300k times.
 +
 +
{|
 +
|- time | requests per millisecond | mysql CPU
 +
|- 30s | 10.0 req/ms | 35%
 +
|- 0.3s | 1095 req/ms | 0%
 +
|}
  
 
== The Resin ClusterCache implementation ==
 
== The Resin ClusterCache implementation ==
  
 
Since Resin's ClusterCache is a persistent cache, the entries you save will be stored to disk and recovered. This means you can store lots of data in the cache without worrying about running out of memory. (LocalCache is also a persistent cache.) If the memory becomes full, Resin will use the cache entries that are on disk. For performance, commonly-used items will remain in memory.
 
Since Resin's ClusterCache is a persistent cache, the entries you save will be stored to disk and recovered. This means you can store lots of data in the cache without worrying about running out of memory. (LocalCache is also a persistent cache.) If the memory becomes full, Resin will use the cache entries that are on disk. For performance, commonly-used items will remain in memory.

Revision as of 00:00, 24 January 2012

Squirrel-48.pngCookbook-48.png


When you want to speed your application by saving the results of long calculations, caching is often a good solution. Fortunately, the Java caching API is being standardized with jcache, and in combination with CDI, you can use caching in a completely standard fashion in Resin. You'll typically want to look at caching when your application starts slowing down, or your database or other expensive resource starts getting overloaded. Caching is useful when you want to:

  • Improve latency
  • Reduce database load
  • Reduce CPU use

This example defines a single local, persistent cache named "my-cache", defined in the WEB-INF/resin-web.xml (this part is Resin specific, of course.) Once the cache is defined, the standard jcache javax.cache.Cache object can be injected into your class with the standard CDI @Inject annotation and used.

The definition selected the ClusterCache implementation (you can also use a LocalCache for a single-server cache), gives it a javax.inject.Named name of "my-cache" and configures it. Here we only configure the name, and set the expire time to 1H. (The default is infinite expire.)

<web-app xmlns="http://caucho.com/ns/resin"
        xmlns:ee="urn:java:ee"
        xmlns:resin="urn:java:com.caucho.resin">

  <resin:ClusterCache ee:Named="my-cache">
    <name>my-cache</name>
    <modified-expire-timeout>1H</modified-expire-timeout>
  </resin:ClusterCache>
 
</web-app>

CDI inject of javax.cache.Cache

In the MyService class, we inject the cache that we defined using the CDI @Inject and @Named annotations. Normally, CDI recommends that you create custom qualifier annotations instead of using @Named, but to keep the example simple, we're giving it a simple name.

The Cache object can be used somewhat like a java.util.Map. Here we just use the get() and put() methods. Because we set the modified-expire-timeout to be 1H, the get() will return null an hour after the data was populated.

import javax.inject.Inject;
import javax.cache.Cache;

public class MyService {
  @Inject @Named("my-cache")
  private Cache<String,String> _cache;

  public String doStuff(String key)
  {
    String cachedValue = _cache.get(key);

    if (cachedValue == null) {
      cachedValue = doLongCalculation(key);

      _cache.put(key, cachedValue);
    }

    return cachedValue;
  }

  public String doLongCalculation(String key)
  {
    // database, REST, XML-parsing, etc.
  }
}

Notice that all the code uses standard APIs without any need to reference Resin classes. Since only the configuration needs to select the Resin cache, your application can easily switch cache implementations.

Cache Performance example

Since reducing database load is a typical cache benefit, it's useful to create a micro-benchmark to see how a cache can help. This is just a simple test with mysql running on the same server and a trivial query. In other words, it's not trying to exaggerate the value of the cache, because almost any real cache use will have a longer "doLongCalculation" than this simple example, and therefore the cache will benefit even more.

The micro-benchmark has a simple jdbc query in the "doLongCalculation" method

"SELECT value FROM test WHERE id=?"

and then to get useful data, the call to "doStuff" is repeated 300k times and compared with the direct call to "doLongCalculation" 300k times.

The Resin ClusterCache implementation

Since Resin's ClusterCache is a persistent cache, the entries you save will be stored to disk and recovered. This means you can store lots of data in the cache without worrying about running out of memory. (LocalCache is also a persistent cache.) If the memory becomes full, Resin will use the cache entries that are on disk. For performance, commonly-used items will remain in memory.

Personal tools
TOOLBOX
LANGUAGES