This section provides tips for tuning various specific types of EJB components:
Depending on the usage of a particular entity bean, one should tune max-cache-size so that beans that are used less (for example, an order that is created and never used after the transaction is over) are cached less, and beans that are used frequently (for example, an item in the inventory that gets referenced very often), are cached more in numbers.
When a stateful bean represents a user, a reasonable max-cache-size of beans is the expected number of concurrent users on the application server process. If this value is too low (in relation to the steady load of users), beans would be frequently passivated and activated, causing a negative impact on the response times, due to CPU intensive serialization and deserialization as well as disk I/O.
Another important variable for tuning is cache-idle-timeout-in-seconds where at periodic intervals of cache-idle-timeout-in-seconds, all the beans in the cache that have not been accessed for more than cache-idle-timeout-in-seconds time, are passivated. Similar to an HTTP session time-out, the bean is removed after it has not been accessed for removal-timeout-in-seconds. Passivated beans are stored on disk in serialized form. A large number of passivated beans could not only mean many files on the disk system, but also slower response time as the session state has to be de-serialized before the invocation.
In high availability mode, when using stateful session beans, consider checkpointing only those methods that alter the state of the bean significantly. This reduces the number of times the bean state has to be checkpointed into the persistent store.
Stateless session beans are more readily pooled than entity or the stateful session beans. Valid values for steady-pool-size, pool-resize-quantity and max-pool-size are the best tunables for these type of beans. Set the steady-pool-size to greater than zero if you want to pre-populate the pool. This way, when the container comes up, it creates a pool with steady-pool-size number of beans. By pre-populating the pool it is possible to avoid the object creation time during method invocations.
Setting the steady-pool size to a very large value can cause unwanted memory growth and can result in large garbage collection times. pool-resize-quantity determines the rate of growth as well as the rate of decay of the pool. Setting it to a small value is better as the decay behaves like an exponential decay. Setting a small max-pool-size can cause excessive object destruction (and as a result excessive object creation) as instances are destroyed from the pool if the current pool size exceeds max-pool-size.
Read-only entity beans cache data from the database. Application Server supports read-only beans that use both bean-managed persistence (BMP) and container-managed persistence (CMP). Of the two types, CMP read-only beans provide significantly better performance. In the EJB lifecycle, the EJB container calls the ejbLoad() method of a read-only bean once. The container makes multiple copies of the EJB component from that data, and since the beans do not update the database, the container never calls the ejbStore() method. This greatly reduces database traffic for these beans.
If there is a bean that never updates the database, use a read-only bean in its place to improve performance. A read-only bean is appropriate if either:
Database rows represented by the bean do not change.
The application can tolerate using out-of-date values for the bean.
For example, an application might use a read-only bean to represent a list of best-seller books. Although the list might change occasionally in the database (say, from another bean entirely), the change need not be reflected immediately in an application.
The ejbLoad() method of a read-only bean is handled differently for CMP and BMP beans. For CMP beans, the EJB container calls ejbLoad() only once to load the data from the database; subsequent uses of the bean just copy that data. For BMP beans, the EJB container calls ejbLoad() the first time a bean is used in a transaction. Subsequent uses of that bean within the transaction use the same values. The container calls ejbLoad() for a BMP bean that doesn’t run within a transaction every time the bean is used. Therefore, read-only BMP beans still make a number of calls to the database.
To create a read-only bean, add the following to the EJB deployment descriptor sun-ejb-jar.xml:
An important parameter for tuning read-only beans is the refresh period, represented by the deployment descriptor entity refresh-period-in-seconds. For CMP beans, the first access to a bean loads the bean’s state. The first access after the refresh period reloads the data from the database. All subsequent uses of the bean uses the newly refreshed data (until another refresh period elapses). For BMP beans, an ejbLoad() method within an existing transaction uses the cached data unless the refresh period has expired (in which case, the container calls ejbLoad() again).
This parameter enables the EJB component to periodically refresh its “snapshot” of the database values it represents. If the refresh period is less than or equal to 0, the bean is never refreshed from the database (the default behavior if no refresh period is given).
If a container-managed relationship (CMR) exists in your application, loading one bean will load all its related beans. The canonical example of CMR is an order-orderline relationship where you have one Order EJB component that has related OrderLine EJB components. In previous releases of the application server, to use all those beans would require multiple database queries: one for the Order bean and one for each of the OrderLine beans in the relationship.
In general, if a bean has n relationships, using all the data of the bean would require n+1 database accesses. Use CMR pre-fetching to retrieve all the data for the bean and all its related beans in one database access.
For example, you have this relationship defined in the ejb-jar.xml file:
<relationships> <ejb-relation> <description>Order-OrderLine</description> <ejb-relation-name>Order-OrderLine</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name> Order-has-N-OrderLines </ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>OrderEJB</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>orderLines</cmr-field-name> <cmr-field-type>java.util.Collection</cmr-field-type> </cmr-field> </ejb-relationship-role> </ejb-relation> </relationships>
When a particular Order is loaded, you can load its related OrderLines by adding this to the sun-cmp-mapping.xml file for the application:
<entity-mapping> <ejb-name>Order</ejb-name> <table-name>...</table-name> <cmp-field-mapping>...</cmp-field-mapping> <cmr-field-mapping> <cmr-field-name>orderLines</cmr-field-name> <column-pair> <column-name>OrderTable.OrderID</column-name> <column-name>OrderLineTable.OrderLine_OrderID</column-name> </column-pair> <fetched-with> <default> </fetched-with> </cmr-field-mapping> </entity-mappping>
Now when an Order is retrieved, the CMP engine issues SQL to retrieve all related OrderLines with a SELECT statement that has the following WHERE clause:
OrderTable.OrderID = OrderLineTable.OrderLine_OrderID
This clause indicates an outer join. These OrderLines are pre-fetched.
Pre-fetching generally improves performance because it reduces the number of database accesses. However, if the business logic often uses Orders without referencing their OrderLines, then this can have a performance penalty, that is, the system has spent the effort to pre-fetch the OrderLines that are not actually needed.
Avoid pre-fetching for specific finder methods; this can often avoid that penalty. For example, consider an order bean has two finder methods: a findByPrimaryKey method that uses the orderlines, and a findByCustomerId method that returns only order information and hence doesn’t use the orderlines. If you’ve enabled CMR pre-fetching for the orderlines, both finder methods will pre-fetch the orderlines. However, you can prevent pre-fetching for the findByCustomerId method by including this information in the sun-ejb-jar.xml descriptor:
<ejb> <ejb-name>OrderBean</ejb-name> ... <cmp> <prefetch-disabled> <query-method> <method-name>findByCustomerId</method-name> </query-method> </prefetch-disabled> </cmp> </ejb>