Message from discussion
Strange Caching Behavior
Received: by 10.224.223.84 with SMTP id ij20mr204544qab.5.1346877471484;
Wed, 05 Sep 2012 13:37:51 -0700 (PDT)
X-BeenThere: ebean@googlegroups.com
Received: by 10.224.209.135 with SMTP id gg7ls252241qab.6.gmail; Wed, 05 Sep
2012 13:37:50 -0700 (PDT)
Received: by 10.58.117.41 with SMTP id kb9mr7105398veb.27.1346877470764;
Wed, 05 Sep 2012 13:37:50 -0700 (PDT)
Received: by 10.58.117.41 with SMTP id kb9mr7105397veb.27.1346877470744;
Wed, 05 Sep 2012 13:37:50 -0700 (PDT)
Return-Path: <robin.bygr...@gmail.com>
Received: from mail-vb0-f50.google.com (mail-vb0-f50.google.com [209.85.212.50])
by gmr-mx.google.com with ESMTPS id ef10si7379vdb.3.2012.09.05.13.37.50
(version=TLSv1/SSLv3 cipher=OTHER);
Wed, 05 Sep 2012 13:37:50 -0700 (PDT)
Received-SPF: pass (google.com: domain of robin.bygr...@gmail.com designates 209.85.212.50 as permitted sender) client-ip=209.85.212.50;
Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of robin.bygr...@gmail.com designates 209.85.212.50 as permitted sender) smtp.mail=robin.bygr...@gmail.com; dkim=pass header...@gmail.com
Received: by vbal1 with SMTP id l1so713253vba.23
for <ebean@googlegroups.com>; Wed, 05 Sep 2012 13:37:50 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20120113;
h=mime-version:in-reply-to:references:date:message-id:subject:from:to
:content-type;
bh=G+oGwFPkzqR0PLNyogp7V8A1IMCY9AwgT8dgt9aj7vw=;
b=xqhrbY38UoiE6/Yko/NaBmifVleJsH8UuTbmJwkp+cayieyRUujjMWC650nK+Gl5mM
Riycy6GNdVjN2K48F5lFQiw6IgUKq9scDKlySw/Pbcc22Yx3UslC5a2ME+9+XZ/ejTcl
XMkOWwlCoQqmumV7reWTafNAwL52Q67RRQ6xhzpHT03GtYSU65ecqH1i8avkpc52Y8Tb
yJ/UluatjuboYpR9vhVeMSyd4EqOwB7aTQT+qpGJeaT8Dgdof13If1AlXUvKrx7bhpJ3
e1afStOeyZRZIv22hb9ypPgx1QpGFgtlBMwbMxlXQEmak+WToOqPzVE/EdA27vzBb2I+
0HVQ==
MIME-Version: 1.0
Received: by 10.58.227.106 with SMTP id rz10mr8247644vec.19.1346877470501;
Wed, 05 Sep 2012 13:37:50 -0700 (PDT)
Received: by 10.58.114.231 with HTTP; Wed, 5 Sep 2012 13:37:50 -0700 (PDT)
Received: by 10.58.114.231 with HTTP; Wed, 5 Sep 2012 13:37:50 -0700 (PDT)
In-Reply-To: <816fe0a2-1fe6-4999-afd7-3b025699974e@googlegroups.com>
References: <ce431293-8288-4a22-9c7d-2e06415b7a6c@googlegroups.com>
<CAC=ts-HRHonr5LsxzgU7GjCu3ofQYYsnw5dxv7E9Y6M7Z1=...@mail.gmail.com>
<45198d8b-b398-4f7d-bc82-76e2fe61d427@googlegroups.com>
<CAC=ts-EiQgM4krxXhp9BXLKGzNSCmFw6wmcXdV9_GDGTXOh...@mail.gmail.com>
<816fe0a2-1fe6-4999-afd7-3b025699974e@googlegroups.com>
Date: Thu, 6 Sep 2012 08:37:50 +1200
Message-ID: <CAC=ts-HwCkwDxdwfxrQvydgq7vUo30t0bGnMiL6-npYGtFT...@mail.gmail.com>
Subject: Re: [ebean] Strange Caching Behavior
From: Rob Bygrave <robin.bygr...@gmail.com>
To: ebean@googlegroups.com
Content-Type: multipart/alternative; boundary=047d7bdc8fb62f6b2f04c8fa5920
--047d7bdc8fb62f6b2f04c8fa5920
Content-Type: text/plain; charset=ISO-8859-1
Good work. I didn't get to look at it last night.
Cheers, Rob.
On Sep 5, 2012 5:21 AM, "mbell697" <mb...@orfoodex.com> wrote:
> I believe I've found the culprit.
>
> The following bit of code in BeanDescriptor.java struct me as dangerous
> since it doesn't make any checks to see if the bean cache was enabled
> explicitly:
>
> private ServerCache getBeanCache() {
> if (beanCache == null) {
> beanCache = cacheManager.getBeanCache(beanType);
> }
> return beanCache;
> }
>
> So i set about trying to trigger it. What I found is that if you get a
> reference to an entity via Ebean.getReference(Entity.class, id) then later
> access a field of that entity it triggers a lazy load pathway that calls
> getBeanCache() creating the cache and causing it to be used for that entity
> from there on out.
>
> The stack trace looks like this when calling entity.getSomeField() and
> breaking in getBeanCache() :
>
> at
> com.avaje.ebeaninternal.server.deploy.BeanDescriptor.getBeanCache(BeanDescriptor.java:1016)
> at
> com.avaje.ebeaninternal.server.deploy.BeanDescriptor.loadFromCache(BeanDescriptor.java:1221)
> at
> com.avaje.ebeaninternal.server.core.DefaultBeanLoader.refreshBeanInternal(DefaultBeanLoader.java:430)
> at
> com.avaje.ebeaninternal.server.core.DefaultBeanLoader.loadBean(DefaultBeanLoader.java:401)
> at
> com.avaje.ebeaninternal.server.core.DefaultServer.loadBean(DefaultServer.java:517)
> at
> com.avaje.ebean.bean.EntityBeanIntercept.loadBeanInternal(EntityBeanIntercept.java:531)
> at
> com.avaje.ebean.bean.EntityBeanIntercept.loadBean(EntityBeanIntercept.java:491)
> at
> com.avaje.ebean.bean.EntityBeanIntercept.preGetter(EntityBeanIntercept.java:623)
>
> Cheers,
>
> Mark
>
> On Tuesday, September 4, 2012 5:47:28 AM UTC-4, Rob Bygrave wrote:
>>
>> *>> Is there something that would enable it automatically?*
>>
>> I don't think so. There is not a property to turn it globally on
>> (although one will be added in the future). So no, not obvious why the bean
>> cache is on. If you add a breakpoint at BeanDescriptor#**cacheInitialise()
>> that might show the cause.
>>
>> It can be turned on at runtime by code like:
>>
>> ServerCacheManager serverCacheManager = Ebean.getServerCacheManager();
>> serverCacheManager.setCaching(**UUOne.class, true);
>>
>> ... but you are unlikely to be doing that.
>>
>>
>> Cheers,Rob.
>>
>>
>> On 4 September 2012 09:47, mbell697 <mb...@orfoodex.com> wrote:
>>
>>> I've set breakpoints inside DefaultServer.java on in the function with
>>> the following signature:
>>>
>>> private <T> T findIdCheckPersistenceContextA**ndCache(Transaction
>>> transaction, BeanDescriptor<T> beanDescriptor, SpiQuery<T> query)
>>>
>>> 2 breakpoints:
>>>
>>> 1) inside the check for an active transaction ( if (t != null) )
>>>
>>> This breakpoint is never hit in my test, which makes sense since
>>> Ebean.find(Entity.class,id); is never called inside a transaction.
>>>
>>> 2) Inside the If statement:
>>>
>>> Object cachedBean = beanDescriptor.cacheGetBean(**query.getId(),
>>> vanilla, query.isReadOnly());
>>> if (cachedBean != null) {
>>> //break here
>>>
>>> If I'm understanding the code correctly, the second bit is looking in
>>> the bean cache.
>>>
>>> This break point isn't hit at all when just loading the page in question
>>> (each load is triggering a call to Ebean.find(Entity.class, id). However
>>> after triggering an action which saves the entity via Ebean.save(entity),
>>> each GET request to load the page does hit this breakpoint which implies (i
>>> think) its coming out of the bean cache.
>>>
>>> Stepping into the preceding check:
>>>
>>> if (!beanDescriptor.**calculateUseCache(query.**isUseBeanCache())) {
>>> // not using bean cache
>>> return null;
>>> }
>>>
>>> seems to indicate the bean cache is active. query.isUseBeanCache() is
>>> null but tracing this through to BeanDescriptor.java there is a beanCache
>>> setup which causes the check to pass.
>>>
>>> So I guess it appears the L2 bean cache is enabled. I guess my question
>>> then becomes why? The docs indicate that it is off by default unless
>>> @CacheStrategy is set on the entity. I've searched my entire code base for
>>> usages of the work 'cache' with no hits related.
>>>
>>> Is there something that would enable it automatically?
>>>
>>> Cheers,
>>>
>>> Mark
>>>
>>>
>>>
>>>
>>> On Monday, September 3, 2012 5:15:42 PM UTC-4, Rob Bygrave wrote:
>>>
>>>> The L1 cache is also known as the persistence context - if you are
>>>> debugging through the code the interface in question is PersistenceContext
>>>> and implementation being DefaultPersistenceContext.
>>>>
>>>>
>>>> *>> L1 cache is actually living not only across requests but also is
>>>> used universally across users.
>>>> *
>>>> No. The PersistenceContext does not live across requests unless the
>>>> beans themselves live across requests (like using your own caching layer or
>>>> something like that). So the beans are being stored in session or something
>>>> like that the persistenceContext will not live across requests.
>>>>
>>>>
>>>> *>> but also is used universally across users.
>>>> *
>>>> No. If beans are shared across threads/requests then sure but you'd
>>>> have to code that. The L2 cache doesn't maintain persistence context
>>>> objects but just the raw data.
>>>>
>>>>
>>>> *>> getting the entity via Ebean.find() by ID the results aren't
>>>> updated
>>>> *
>>>> I'd suggest you attach a debugger and step through the find().
>>>>
>>>>
>>>>
>>>> On 4 September 2012 05:51, mbell697 <mb...@orfoodex.com> wrote:
>>>>
>>>>> I've run into a bit of a quandary and am hoping for some insight into,
>>>>> what I believe, is the longevity/sharability of the L1 cache.
>>>>>
>>>>> I've noticed that if I make a change to the database manually, outside
>>>>> Ebean or from another server also using Ebean, the results do update as a
>>>>> result of queries, that is looking at a paginglist of entites I see the
>>>>> updates, however on a page getting the entity via Ebean.find() by ID the
>>>>> results aren't updated, implying the bean is coming from a cache somewhere.
>>>>>
>>>>>
>>>>> At first I assumed that the L2 cache was enabled by default and was to
>>>>> blame but I haven't set @CacheStrategy anywhere implying it should be
>>>>> disabled.
>>>>>
>>>>> This left me with the bean coming from the L1 cache somehow, but even
>>>>> if I completely log out (destroying any and all session data), or login
>>>>> from a different machine (brand new session), I find the same caching
>>>>> behavior. I'm starting to wonder if the L1 cache is actually living not
>>>>> only across requests but also is used universally across users.
>>>>>
>>>>> Its not clear to me exactly when the L1 cache expires from the
>>>>> documentation, it does indicate it lives through a transaction but there
>>>>> are certainly no transactions open for this period of time, after 10-15
>>>>> minutes ish the caching behavior disappears. The docs also indicate that
>>>>> it will live on for lazy loading purposes but I have no idea when it 'ends'
>>>>> (when all beans in the cache are garbage collected?).
>>>>>
>>>>> This issue may be caused by the usage of JSF's ViewScope in
>>>>> combination with not being able to guarantee when a view will be destroy
>>>>> (sending the entity bean siting in the controller to garbage collection).
>>>>> Is the reference to a bean sitting in an unterminated view scope causing
>>>>> the L1 to stick around for all users?
>>>>>
>>>>> Even further this would seem to imply that even if everything were
>>>>> completely stateless if two requests were in flight they would share a L1
>>>>> cache? If you had a string of requests for the same entity on multiple
>>>>> servers such that the entity kept getting accessed, perpetuating the L1
>>>>> cache I would think the caches could get wildly out of sync, which brings
>>>>> up my next concern, I intentionally triggered an optimistic lock exception
>>>>> on the entity I was testing this issue with and the caching effect
>>>>> continued after. I would have thought at the very least an OLE would have
>>>>> flushed all caches. If it doesn't I can easily see issues with multiple
>>>>> servers handling requests that keep the L1 cache alive never getting
>>>>> flushed and brought up to date.
>>>>>
>>>>> Any thoughts on whats going on here?
>>>>>
>>>>> Cheers,
>>>>>
>>>>> Mark
>>>>>
>>>>>
>>>>>
>>>>
>>
--047d7bdc8fb62f6b2f04c8fa5920
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
<p>Good work. I didn't get to look at it last night. </p>
<p>Cheers, Rob.</p>
<div class=3D"gmail_quote">On Sep 5, 2012 5:21 AM, "mbell697" <=
;<a href=3D"mailto:mb...@orfoodex.com">mb...@orfoodex.com</a>> wrote:<br=
type=3D"attribution"><blockquote class=3D"gmail_quote" style=3D"margin:0 0=
0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
I believe I've found the culprit.<div><br></div><div>The following bit =
of code in BeanDescriptor.java struct me as dangerous since it doesn't =
make any checks to see if the bean cache was enabled explicitly:</div><div>
<br></div><div><div>=A0private ServerCache getBeanCache() {</div><div>=A0 =
=A0 if (beanCache =3D=3D null) {</div><div>=A0 =A0 =A0 beanCache =3D cacheM=
anager.getBeanCache(beanType);</div><div>=A0 =A0 }</div><div>=A0 =A0 return=
beanCache;</div><div>=A0 }</div>
<div><br></div><div>So i set about trying to trigger it. =A0What I found is=
that if you get a reference to an entity via Ebean.getReference(Entity.cla=
ss, id) then later access a field of that entity it triggers a lazy load pa=
thway that calls getBeanCache() creating the cache and causing it to be use=
d for that entity from there on out. =A0</div>
<div><br></div><div>The stack trace looks like this when calling entity.get=
SomeField() and breaking in getBeanCache() :</div><div><br></div><div><div>=
=A0 =A0 =A0 =A0 =A0 at com.avaje.ebeaninternal.server.deploy.BeanDescriptor=
.getBeanCache(BeanDescriptor.java:1016)</div>
<div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebeanint=
ernal.server.deploy.BeanDescriptor.loadFromCache(BeanDescriptor.java:1221)<=
/div><div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebe=
aninternal.server.core.DefaultBeanLoader.refreshBeanInternal(DefaultBeanLoa=
der.java:430)</div>
<div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebeanint=
ernal.server.core.DefaultBeanLoader.loadBean(DefaultBeanLoader.java:401)</d=
iv><div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebean=
internal.server.core.DefaultServer.loadBean(DefaultServer.java:517)</div>
<div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebean.be=
an.EntityBeanIntercept.loadBeanInternal(EntityBeanIntercept.java:531)</div>=
<div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebean.be=
an.EntityBeanIntercept.loadBean(EntityBeanIntercept.java:491)</div>
<div><span style=3D"white-space:pre-wrap"> </span> =A0at com.avaje.ebean.be=
an.EntityBeanIntercept.preGetter(EntityBeanIntercept.java:623)</div></div><=
div><br></div><div>Cheers,</div><div><br></div><div>Mark</div><br>On Tuesda=
y, September 4, 2012 5:47:28 AM UTC-4, Rob Bygrave wrote:<blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex">
<i>>> Is there something that would enable it automatically?</i><br><=
br>I don't think so.=A0 There is not a property to turn it globally on =
(although one will be added in the future). So no, not obvious why the bean=
cache is on. If you add a breakpoint at BeanDescriptor#<u></u>cacheInitial=
ise() that might show the cause.<br>
<br>It can be turned on at runtime by code like:<br><br>ServerCacheManager =
serverCacheManager =3D Ebean.getServerCacheManager();<br>serverCacheManager=
.setCaching(<u></u>UUOne.class, true);<br><br>... but you are unlikely to b=
e doing that. =A0=A0 <br>
<br><br>Cheers,Rob.<br><br><br><div class=3D"gmail_quote">On 4 September 20=
12 09:47, mbell697 <span dir=3D"ltr"><<a>mb...@orfoodex.com</a>></spa=
n> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;b=
order-left:1px #ccc solid;padding-left:1ex">
I've set breakpoints inside DefaultServer.java on in the function with =
the following signature:<div><br></div><div>private <T> T findIdCheck=
PersistenceContextA<u></u>ndCache(Transaction transaction, BeanDescriptor&l=
t;T> beanDescriptor, SpiQuery<T> query)</div>
<div><br></div><div>2 breakpoints: =A0</div><div><br></div><div>1) inside t=
he check for an active transaction ( if (t !=3D null) )</div><div><br></div=
><div>This breakpoint is never hit in my test, which makes sense since Ebea=
n.find(Entity.class,id); is never called inside a transaction.</div>
<div><br></div><div>2) Inside the If statement:</div><div><br></div><div>Ob=
ject cachedBean =3D beanDescriptor.cacheGetBean(<u></u>query.getId(), vanil=
la, query.isReadOnly());</div><div>=A0 =A0 if (cachedBean !=3D null) {</div=
><div>
=A0 =A0 =A0 //break here</div>
<div><br></div><div>If I'm understanding the code correctly, the second=
bit is looking in the bean cache. =A0</div><div><br></div><div>This break =
point isn't hit at all when just loading the page in question (each loa=
d is triggering a call to Ebean.find(Entity.class, id). =A0However after tr=
iggering an action which saves the entity via Ebean.save(entity), each GET =
request to load the page does hit this breakpoint which implies (i think) i=
ts coming out of the bean cache.=A0</div>
<div><br></div><div>Stepping into the preceding check:=A0</div><div><br></d=
iv><div><div>if (!beanDescriptor.<u></u>calculateUseCache(query.<u></u>isUs=
eBeanCache())) {</div><div>=A0 =A0 =A0 // not using bean cache</div><div>=
=A0 =A0 =A0 return null;</div>
<div>=A0 =A0 }</div></div><div><br></div><div>seems to indicate the bean ca=
che is active. =A0query.isUseBeanCache() is null but tracing this through t=
o BeanDescriptor.java there is a beanCache setup =A0which causes the check =
to pass.</div>
<div><br></div><div>So I guess it appears the L2 bean cache is enabled. =A0=
I guess my question then becomes why? =A0The docs indicate that it is off b=
y default unless @CacheStrategy is set on the entity. =A0I've searched =
my entire code base for usages of the work 'cache' with no hits rel=
ated.</div>
<div><br></div><div>Is there something that would enable it automatically?<=
/div><div><br></div><div>Cheers,</div><div><br></div><div>Mark</div><div><b=
r></div><div><br></div><div><br></div><div><div><br>On Monday, September 3,=
2012 5:15:42 PM UTC-4, Rob Bygrave wrote:</div>
<blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;borde=
r-left:1px #ccc solid;padding-left:1ex"><div>The L1 cache is also known as =
the persistence context - if you are debugging through the code the interfa=
ce in question is PersistenceContext and implementation being DefaultPersis=
tenceContext. <br>
<br><br><i>>> L1 cache is actually living not only across requests b=
ut also is used universally across users.=A0 <br>
</i><br>No. The PersistenceContext does not live across requests unless the=
beans themselves live across requests (like using your own caching layer o=
r something like that). So the beans are being stored in session or somethi=
ng like that the persistenceContext will not live across requests.<br>
<br><br><i>>> but also is used universally across users.=A0 <br></i>=
<br>No. If beans are shared across threads/requests then sure but you'd=
have to code that. The L2 cache doesn't maintain persistence context o=
bjects but just the raw data.<br>
<br><br><i>>> getting the entity via Ebean.find() by ID the results =
aren't updated<br></i><br>I'd suggest you attach a debugger and ste=
p through the find().<br><br><br><br></div><div><div class=3D"gmail_quote">
On 4 September 2012 05:51, mbell697 <span dir=3D"ltr"><<a>mb...@orfoodex=
.com</a>></span> wrote:<br>
<blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p=
x #ccc solid;padding-left:1ex">I've run into a bit of a=A0quandary=A0an=
d am hoping for some insight into, what I believe, is the longevity/sharabi=
lity of the L1 cache.<div>
<br></div><div>I've noticed that if I make a change to the database man=
ually, outside Ebean or from another server also using Ebean, the=A0results=
do update as a result of queries, that is looking at a paginglist of entit=
es I see the updates, however on a page getting the entity via Ebean.find()=
by ID the results aren't updated, implying the bean is coming from a c=
ache somewhere. =A0=A0=A0</div>
<div><br></div><div>At first I assumed that the L2 cache was enabled by def=
ault and was to blame but I haven't set @CacheStrategy anywhere implyin=
g it should be disabled.</div><div><br></div><div>This left me with the bea=
n coming from the L1 cache somehow, but even if I completely log out (destr=
oying any and all session data), or login from a different machine (brand n=
ew session), I find the same caching behavior. =A0I'm starting to wonde=
r if the L1 cache is actually living not only across requests but also is u=
sed universally across users. =A0</div>
<div><br></div><div>Its not clear to me exactly when the L1 cache expires f=
rom the documentation, it does indicate it lives through a transaction but =
there are certainly no transactions open for this period of time, after 10-=
15 minutes ish the caching=A0behavior=A0disappears. =A0The docs also indica=
te that it will live on for lazy loading purposes but I have no idea when i=
t 'ends' (when all beans in the cache are garbage collected?). =A0<=
/div>
<div><br></div><div>This issue may be caused by the usage of JSF's View=
Scope in combination with not being able to=A0guarantee=A0when a view will =
be destroy (sending the entity bean siting in the controller to garbage col=
lection). =A0Is the reference to a bean sitting in an unterminated view sco=
pe causing the L1 to stick around for all users?</div>
<div><br></div><div>Even further this would seem to imply that even if ever=
ything were completely stateless if two requests were in flight they would =
share a L1 cache? =A0If you had a string of requests for the same entity on=
multiple servers such that the entity kept getting accessed, perpetuating =
the L1 cache I would think the caches could get wildly out of sync, which b=
rings up my next concern, I intentionally triggered an optimistic lock exce=
ption on the entity I was testing this issue with and the caching effect co=
ntinued after. =A0I would have thought at the very least an OLE would have =
flushed all caches. =A0If it doesn't I can easily see issues with multi=
ple servers handling requests that keep the L1 cache alive never getting fl=
ushed and brought up to date.</div>
<div><br></div><div>Any thoughts on whats going on here?</div><div><br></di=
v><div>Cheers,</div><div><br></div><div>Mark</div><div><br></div><div><br><=
/div></blockquote></div><br>
</div></blockquote></div></blockquote></div><br>
</blockquote></div></blockquote></div>
--047d7bdc8fb62f6b2f04c8fa5920--