CMP Performance
Resin 3.0

Features
Installation
Configuration
Web Applications
IOC/AOP
Resources
JSP
Quercus
Servlets and Filters
Databases
Admin (JMX)
CMP
EJB
Amber
EJB 3.0
Security
XML and XSLT
XTP
JMS
Performance
Protocols
Third-party
Troubleshooting/FAQ

User's Guide
Reference
Tutorials
Scrapbook

EJB Misconceptions
CMP Relations Catalog
CMP Performance
CMP Relations Catalog
User's Guide
Reference

  1. Caching Pages and Subpages
  2. read-only methods
  3. read-only beans
  4. Bean cache-timeout
  5. Caching JNDI lookup of EJBLocalHome values
  6. Using OFFSET and LIMIT for large queries
  7. Loading beans in queries
  8. Conclusion

Note: This tutorial requires Resin-CMP 2.1.1 or later.

Improving performance generally means eliminating unnecessary operations. Resin-CMP offers a number of techniques for improving performance. As with all performance work, it's best to use a profiler with a representative load to find out where most of the time is being taken.

Caching Pages and Subpages

A very effective technique for improving performance is to use Resin's proxy caching to cache entire pages. Caching a heavily used page for as short a time as 60 seconds can avoid most of the database calls.

Even if the entire page can't be cached, it's normally possible to cache subpages. Any include() page is cached in the exact same way as the main page.

Resin's page caching uses the HTTP caching headers. The simplest example just sets the expires time to 60 seconds in the future. The technique is identical for both servlets and JSP pages.

Expiring a page 60 seconds in the future
public void service(HttpServletRequest request,
                    HttpServletResponse response)
  throws IOException, ServletException
{
  long now = System.currentTimeMillis();

  response.setDateHeader("Expires", now + 60000L);

  ...
}

read-only methods

Most database queries read data from the database; updating the database is more rare. Because reads can be cached, it's important to tell Resin which business methods are expected to be read methods (transactions) and which are update methods (transactions).

Read transactions can use cached values. Even a short cache timeout like 10s can save many database queries. In many cases, read transactions don't need to query the database at all.

Update transactions must read the data from the database, update the data, and store it back to the database.

By default, Resin knows that findXXX, ejbSelectXXX, and cmp-field and cmr-field getXXX methods are read transactions. It assumes that all other business methods are update transactions.

For example, the following sum method adds the values of cmp-fields a and b. Because it only reads data, it could use Resin's cache, but because it's a business method, Resin can't automatically determine that it's a read-only method. By declaring it read-only, the application can cache and improve performance.

read-only method
public int sum()
{
  return getA() + getB();
}

Resin-CMP supports read-only transactions by letting application mark business methods as read-only. If a transaction is read-only, Resin can use a cached value. If the transaction is read-write, Resin will need to load the data from the database to ensure the data is properly locked.

The <method> configuration with the <resin-isolation> tag configures read-only methods. The read-only value is used for read methods and the database value is used for read-write methods, i.e. methods that need to load from the database to ensure locking.

Most Methods are read-only
<!-- methods are read-only by default -->
<method>
  <signature>*</signature>
  <resin-isolation>read-only</resin-isolation>
</method>

<!-- update is a read-write method -->
<method>
  <signature>update(String)</signature>
  <resin-isolation>database</resin-isolation>
</method>

The * value for the signature specifies the default value.

Resin-CMP knows the setXXX, create, and remove methods are update. Resin-CMP will always use database for those methods, ignoring the resin-isolation value. Resin-CMP also knows the getXXX methods and findXXX methods are read-only, so it can ignore the resin-isolation value. So applications only need to supply values for the business methods.

read-only beans

Some database tables never change, or are only changed by infrequent external maintenance so the CMP read-only. Beans for read-only tables can always cache the bean values even inside an "update" transaction. (See above for read-only vs update transactions.)

Read-only entity beans are marked with the <read-only> attribute.

Even when a read-only bean is in an updating transaction, i.e. other beans are changing, the read-only bean can use the cached value.

Bean cache-timeout

The cache-timeout configures how long Resin can cache a loaded bean. The default is only 5 seconds. Although that's short, it ensures that a single page that reads the uses entity bean several times will only access the database once. For beans like the category example, a longer value like an hour may be more appropriate.

resin.ejb for a read-only bean
<entity>
  <ejb-name>category</ejb-name>

  <cache-timeout>15m</cache-timeout>
</entity>

Caching JNDI lookup of EJBLocalHome values

Because the EJBLocalHome object is a constant object, servlets should load it in the init() method and use that loaded value in the servlet. Because this optimization is so simple, all servlets should use it as their default EJB use pattern.

Caching the EJBLocalHome in the servlet
public class MyServlet extends HttpServlet {
  private FooHome fooHome;

  public void init()
    throws ServletException
  {
    try {
      Context env = (Context) new InitialContext().lookup("java:comp/env");

      fooHome = (FooHome) env.lookup("cmp/foo");
    } catch (Exception e) {
      throw new ServletException(e);
    }
  }

  public void service(HttpServletRequest request,
                      HttpServletResponse response)
    throws IOException, ServletException
  {
    // now use the fooHome
  }

  ...
}

Using OFFSET and LIMIT for large queries

Some databases can limit queries to a short subsequence of the entire query. For example, a search engine list will normally have several pages of results. Normally, the OFFSET and LIMIT will also use ORDER BY to make sure the results returned are consistent.

OFFSET and LIMIT
SELECT o FROM test o ORDER BY o.id OFFSET ?1 LIMIT ?2

Loading beans in queries

By default, Resin-CMP will load the bean values when it runs the query. For example, a findAll method will load all the beans with a single SQL SELECT. The following getXXX methods will not need to access the database.

Normally, this preloading will provide the best performance. In some cases, only the identity of the loaded beans is important, not the data values. In those cases, the query should only ask the database for the primary key, not the entire bean's state.

The loading is configured with the <query-loads-bean> tag.

ejbSelectAll with no preload
<method>
  <signature>ejbSelectAll()</signature>
  <query-loads-bean>false</query-loads-bean>
</method>

Conclusion

The performance techniques described above give the application developer great flexibility for improving CMP performance. As with all performance work, it's always important to use a profiler to find the bottlenecks in the application. Very often, the real bottleneck will be a surprise.


CMP Relations Catalog
User's Guide
Reference
Copyright © 1998-2006 Caucho Technology, Inc. All rights reserved.
Resin® is a registered trademark, and HardCoretm and Quercustm are trademarks of Caucho Technology, Inc.