Hello Wes,
thanks for that analysis, I must admit I haven't really thought of what
this means with optimistic txs!
While it is good that a stale read under optimistic transactions won't
lead to wrong persistent data under any circumstances (your #2), I'd
rather like to avoid optimistic verification exceptions where possible,
though.
Concerning #1: the point with reading from P-NT instances outside
transactions for me is that read-requests can be satisfied from cache
without round-tripping to the DB. For a typical web-application that you
can browse, and where most requests are read-requests, I find this the
most effective. Now if my P-NT instances are never invalidated/hollowed
out, e.g. because they never take part in a datastore transaction, I
might be showing wrong information to the user, which, depending on the
application, can be highly undesired.
The next thing I'd consequently ask for is that P-NT instances are in
fact made transactionally consistent, in that they should also be
hollowed out automatically if they have siblings by id that went from
P-dirty to P-clean in some transaction. That's what I currently must
imitate in a clumsy way after each transaction
This all would make P-NT much more safe to use, and more useful in
consequence, IMHO...
Regards,
Jörg
Wes Biggs schrieb:
I agree that it would be nice to change the method signatures to
"evictById" for those that take OIDs in order to avoid confusion.
To clarify what I mean about persistent nontransactional objects, see
section 5.6.1 of the spec:
"A persistent-nontransactional instance transitions to
persistent-clean if any managed field
is accessed when a datastore transaction is in progress. The state of
the instance in memory
is discarded and the state is loaded from the datastore."
If you are running with an optimistic transaction instead, you'll get
an optimistic verification exception at commit time. So I guess it is
possible to read stale data from the instance in the PM cache under a
couple of scenarios:
1. Reading previously loaded fields of a P-NT instance outside of a
transaction.
2. Reading previously loaded fields of a P-NT instance inside an
optimistic transaction.
In these cases, I think you're right that it would be necessary to
hollow the instances in order to be absolutely sure that no stale data
is read after a L2 cache evict().
On the other hand, if you're in an optimistic transaction, don't you
want to retain the previously read values (they represent the ACID
guarantee from the optimistic transaction)? So the only case where it
might make sense to me is #1 above, and that seems debatable to me.
Do most people using P-NT objects expect them to be consistent with
the L2 cache at all times? Or are they expected to act like a limited
form of an optimistic transaction?
I don't have a strong opinion about this, I'm just trying to fully
articulate the question.
Wes
Joerg von Frantzius wrote:
Hi Wes,
thanks for your answer, please see my comments below.
Wesley Biggs schrieb:
Joerg von Frantzius wrote:
The problem here is that either evict() accepts only PC objects,
not object ids, so we have to call PM.getObjectById() beforehand.
If no object for that id was present, we're instantiating a hollow
object here only to discard it afterwards, that's not very effective.
I'm not quite parsing your "either" here, sorry. But
DataStoreCache.evict() accepts object IDs. I'm not sure I see the
necessity of calling PM.evict() as well, unless you have some
particularly long-lived transactions.
We're doing nontransactional reads on long-living objects, so I
guessed we needed to call PM.evict() to avoid accessing stale field
data.
You're of course right about DatastoreCache.evict() accepting IDs,
thanks for pointing that out. I had just seen the same method
signature, and so I assumed the parameter semantics also being the same.
Calling it evictById() probably would be less misleading, even more
so as a mistake here won't show up immediately. Also, if you only
have a jar without sourcecode, the signatures are absolutely
indistinguishable (Which of course is not an excuse for not having
read the spec thoroughly enough ;)
As we really want cache invalidation here, not eviction, this is
even worse. For this purpose, it would be far more convenient to
have some method like invalidateCachesFor(Object id) on
PersistenceManagerFactory.
That's the intention of DataStoreCache.evict(). The semantics are
different than PM.evict().
Only now I start understanding that I was misled by the word evict()
for the L2-cache: as the user never gets hold of an L2 cache object
anyway (a L1-cache object will be created for that), he shouldn't
need to care whether the L2 cache internally needs to throw away
(evict) some object in order to invalidate cached state. Spec says
"/The evict methods are hints to the implementation that the
instances referred to by the object ids are stale and should be
evicted from the cache./" It might be nit-picking, but I think it
would be clearer if the method was called invalidateByÍd(), which
would be natural for some cache interface, and if the explanation
said "/that the object state referred to by the object ids should be
discarded/"
Also, the spec doesn't say anything about DatastoreCache.evict()
having any impact on P-nontrans instances. So I still need to go to
every PM and evict there as well, which is very inconvenient.
Or does the "evict" row in table 2 for P-nontrans really apply to
/both /evict() methods, not only PM.evict()!? The RI JPOX isn't doing
anything like that, by the way.
To make our wish complete ;) this method would transition all
non-transactional instances to hollow for that id, for all the PMs
the PMF has given out. All transactional objects with that id
should be transitioned to hollow after their transaction has
completed (either with commit or rollback).
Persistent nontransactional instances will have to be revalidated
against the datastore (or cache thereof) before being re-enlisted in
a transaction anyway. The behavior you mention is a good way to
implement that, but it doesn't need to be mandated (hollow is not a
user-visible state).
I'm not sure what you mean by mandating here? I'd just like to make
sure that invalidated non-transactional instances will reload state
upon next read access, without having to iterate all PMs. Also, I'd
rather not like a call to PM.getObjectById() afterwards returning a
new Java object for the same id, which I guess is the case after
calling PM.evict(PM.getObjectById(id)).
If a method invalidateById() existed, I'd see the sense of evict() in
releasing the associated memory. evict() currently does two things at
same time: evicting and transitioning to hollow. For (distributed)
cache invalidation, I find it sensible to desire only the latter.
Regards,
Jörg