March 29, 2005
Not very popular choice for persistence nowadays, CMP still too often is only
readily available persistence framework in Enterprise environment. While not
as sexy as hibernate or SQL Maps, CMP 2.X implementation
in leading application servers such as BEA Weblogic offers
quite good solution for ORM problems. Beside there is a lots of developers
familiar with EJB and mature support from different tools from xdoclet to Eclipse wtp/jst.
Let's
take a look at what can be done to improve performance of CMP in Weblogic
by enabling more aggressive than default caching strategies.
Everybody knows that EJB Container maintains cache or pool of Entity Beans,
usually configurable in deployment descriptor. Amazingly lots of developers
don't realize that it doesn't mean that once Container loaded particular bean
instance from database it won't go to database again while bean instance is
kept in pool. Quite contrary, by default Container calls ejbLoad() to
synchronize instance's state from the database at the beginning of every transaction.
Basically on every operation with CMP bean, even if bean was loaded seconds
ago in previous transaction Container executes SQL select to refresh it. Only
while you operating with CMP instance(s) in one transaction Container caches
them. This is so called 'Commit Options A/B/C' as described in EJB 2.0 specification
(10.5.9).
Obviously reloading state from database in every transaction could have some
performance impact. Easy to understand why this is default behavior - this
is safest way if database shared between multiple processes and each one of
them could change state of persisted object in database while it's cached in
EJB pool. In this case we're facing possibility of lost updates - other process
updates record in database and then Container overwrites it with potentially
stale data from cache. But first of all refreshing data in the beginning of
transaction doesn't guarantee against lost updates, it's just reduces possibility
of it. This is a well know problem in database world and there are two approaches
to deal with it. First approach is so called 'pessimistic concurrency locking',
when records in database locked for other updaters for duration of transaction
('select for update'). This is almost never used because of significant overhead
and performance impact. Second approach is called 'optimistic concurrency locking',
in this case there is no actual locks held in database but process which execute
update has some mechanism to detect that record been updated by other process
between his read and update. Preferred way to implement this is with 'version'
column on the database table (in SQL update increment value for that column,
include previous value in 'where' clause and then detect if no rows are updated).
Weblogic has built in support
for optimistic concurrency in CMP beans via verify-columns element
in weblogic-cmp-rdbms-jar.xml deployment descriptor. It is a good practice
to have 'version' column in all (updatable) tables in your database and configure
CMPs to use it by enabling optimistic concurrency in concurrency-strategy element
in weblogic-ejb-jar.xml
Optimistic concurrency alone doesn't give you any performance improvement,
but it's necessary step to enable cache-between-transactions functionality
which is not available without enabling optimistic concurrency (for the reasons
discussed above). Setting cache-between-transactions to 'true' will result
in Container skipping calls to ejbLoad() if bean instance is already
available in the EJB cache. For certain types of applications where the same
objects accessed more than once from different transactions over short period
of time this can lead to significant performance improvements (in our tests
up to 30% for our application).
Naturally your application must be ready to deal with OptimisticConcurrencyException when
concurrency violation is detected by container. When OptimisticConcurrencyException (subtype
of RuntimeException) is thrown Container discards EJB instance from cache.
In a cluster, when a bean with Optimistic concurrency is updated, notifications
are broadcast to other cluster members to prevent optimistic conflicts. Note
that if you have delay-updates-until-end-of-tx set
to 'true' (default) your code won't see optimistic exception until transaction
commit.
Word of caution. While available for quite a long time, cache-between-transactions
functionality never worked stable in Weblogic 7, but it seems to be fixed in
version 8.1. We're using it in production applications and quite happy with
the results.
About the author
Dmitri Maximovich
Blog: http://www.jroller.com/page/maximdim
Dmitri Maximovich is an independent consultant specializing in software design, development, and technical training. He has more than twelve years of industry experience and has been involved with J2EE since its inception. His job profile includes designing and developing mission critical applications for financial and pharmaceutical industries.
Dmitri lives in Toronto, Canada and has a master's degree in Physics from St. Petersburg university.
|