Using JBC as a 2nd level cache leads to problems deserializing user classes when the
Hibernate query cache is enabled. See
http://jira.jboss.com/jira/browse/JBCLUSTER-150 for
details.
This is really a JBC/Hibernate issue, although workarounds are possible in EJB3. Since any
solution impacts EJB3, and any very short term workaround (i.e. for a December stacks
release) is only possible in EJB3, I'm discussing the issue here.
Issue is JBC's classloader is from server/all/lib, and thus can't see and
deserialize classes that are loaded from deploy. Deserialization happens when
Hibernate's use of JBC as 2nd level cache causes replication. Specifically:
1) The Hibernate query cache is used. Here a user class could end up being used as an
argument in a query. Hibernate replicates the actual query as the part of the FQN of a
node in JBC.
2) A custom class is used as a field in an entity, and its not mapped as a component, but
rather Hibernate treates it as a BLOB. The custom class would be replicated. (I
haven't actually confirmed this failure mode, but it quite likely exists.)
Solutions:
A) Register a classloader with JBC for a region of the cache. JBC exposes an API to allow
this to happen. The Hibernate/JBC integration code (part of Hibernate) can be updated to
take advantage of this API. Hibernate already has the logical concept of different cache
regions; the integration code can be updated to register the thread context classloader
with JBC when a region is created.
EJB3 already partially overrides the standard Hibernate/JBC integration code, so as a
short term workaround it's possible to fix this in EJB3, pending a later Hibernate
release with this fixed.
Problems with solution A):
i) Hibernate has a "default" query cache region that will be shared by all
queries that don't specifically name a region. There is no foolproof way to assign a
specific classloader to this region, since it's storing to a JBC instance that may be
shared between multiple EJB deployments. The cache region can be specified by users,
although it's ugly:
| @Entity
| @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
| @NamedQueries({
| @NamedQuery(name="account.highbalance.default",query="select
account.balance from Account as account where account.accountHolder = ?1 and
account.balance > ?2"),
| @NamedQuery(name="account.highbalance.namedregion",query="select
account.balance from Account as account where account.accountHolder = ?1 and
account.balance > ?2",
|
hints={(a)QueryHint(name="org.hibernate.cacheRegion",value="AccountRegion")})
| })
| public class Account implements Serializable
| {
| ...
| }
|
The second query above uses the named region.
ii) With the query cache, the custom class is actually part of the Fqn of a JBC node,
rather than part of its data map. I realized over the weekend that the existing JBC code
for handling region-based classloaders doesn't handle custom classes in an Fqn. JIRA
to remove this limitation is
http://jira.jboss.com/jira/browse/JBCACHE-876, which should
be pretty straightforward. But, fixing it requires JBC 1.4.1.GA, which is likely at least
a couple weeks away.
Bringing us to solution..
B) Don't replicate the query cache.
Hibernate provides a hook to specifying the factory class used to set up any query cache.
In persistence.xml we can specify a custom query cache factory that integrates with JBC,
but which instructs JBC not to replicate any writes for that region of the cache. Thus
the query cache is a local only cache, while entities are replicated. Hibernate also has
an "UpdateTimestampsCache", which is used to trigger invalidations of query
cache entries. AFAIK, there is no replication problem with this cache, and it needs to
remain replicated.
I've pinged the Hibernate guys re: any issues w/ not replicating the query cache.
Solution B can also be implemented in the EJB3 code base pending a better solution in a
Hibernate release. If there is an immediate requirement to get a workaround for this
problem, it's the only way I see to go.
Another long term possibility is solution...
C) Like A), but now we create one JBC entity cache per deployment (i.e. per Hibernate
session factory) rather than a single shared cache. This avoids the problem of deciding
what classloader to use for the "default" query cache -- there's only one
classloader per cache instance. With the JGroups multiplexer, having numerous JBC
instances is a workable option. But going this route requires more thought.
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3989163#...
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&a...