<document>
  <header>
    <product>resin-ee</product>
    <resin-2.0>$ee/ejb-tut/cmp-performance.xtp</resin-2.0>
    <title>CMP Performance</title>
  </header>

  <body>
    <summary/>

<note>This tutorial requires Resin-CMP 2.1.1 or later.</note>

<p>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.</p>

<section title="Caching Pages and Subpages">

<p>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.</p>

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

<p>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.</p>

<example title="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);

  ...
}
</example>

</section>

<section title="read-only methods">

<p>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).</p>

<p>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.</p>

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

<p>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.</p>

<p>For example, the following <code>sum</code> method adds the values
of cmp-fields <var/a/> and <var/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.</p>

<example title="read-only method">
public int sum()
{
  return getA() + getB();
}
</example>

<p>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.</p>

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

<example title="Most Methods are read-only">
&lt;!-- methods are read-only by default -->
&lt;method>
  &lt;signature>*&lt;/signature>
  &lt;resin-isolation>read-only&lt;/resin-isolation>
&lt;/method>

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

<p>The <var/*/> value for the signature specifies the default value.</p>

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

</section>

<section title="read-only beans">

<p>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.)</p>

<p>Read-only entity beans are marked with the &lt;read-only> attribute.</p>

<p>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.</p>

</section>

<section title="Bean cache-timeout">

<p>The <var/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.</p>

<example title="resin.ejb for a read-only bean">
&lt;entity>
  &lt;ejb-name>category&lt;/ejb-name>

  &lt;cache-timeout>15m&lt;/cache-timeout>
&lt;/entity>
</example>

</section>

<section title="Caching JNDI lookup of EJBLocalHome values">

<p>Because the <code/EJBLocalHome/> object is a constant object,
servlets should load it in the <code/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.</p>

<example title="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
  }

  ...
}
</example>

</section>

<section title="Using OFFSET and LIMIT for large queries">

<p>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.</p>

<example title="OFFSET and LIMIT">
SELECT o FROM test o ORDER BY o.id OFFSET ?1 LIMIT ?2
</example>

</section>

<section title="Loading beans in queries">

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

<p>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.</p>

<p>The loading is configured with the &lt;query-loads-bean> tag.</p>

<example title="ejbSelectAll with no preload">
&lt;method>
  &lt;signature>ejbSelectAll()&lt;/signature>
  &lt;query-loads-bean>false&lt;/query-loads-bean>
&lt;/method>
</example>

</section>

<section title="Conclusion">

<p>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.</p>

</section>

  </body>
</document>
