Stepping back a little, some history and theory of the entity cache might be 
helpful.

The original intent of the entity cache was a simple way to keep frequently 
used values/records closer to the code that uses them, ie in the application 
server. One real world example of this is the goal to be able to render 
ecommerce catalog and product pages without hitting the database.

Over time the entity caching was made more complex to handle more caching 
scenarios, but still left to the developer to determine if caching is 
appropriate for the code they are writing.

In theory is it possible to write an entity cache that can be used 100% of the 
time? IMO the answer is NO. This is almost possible for single record caching, 
with the cache ultimately becoming an in-memory relational database running on 
the app server (with full transaction support, etc)... but for List caching it 
totally kills the whole concept. The current entity cache keeps lists of 
results by the query condition used to get those results and this is very 
different from what a database does, and makes things rather messy and 
inefficient outside simple use cases.

On top of these big functional issues (which are deal killers IMO), there is 
also the performance issue. The point, or intent at least, of the entity cache 
is to improve performance. As the cache gets more complex the performance will 
suffer, and because of the whole concept of caching results by queries the 
performance will be WORSE than the DB performance for the same queries in most 
cases. Databases are quite fast and efficient, and we'll never be able to 
reproduce their ability to scale and search in something like an in-memory 
entity cache, especially not considering the massive redundancy and overhead of 
caching lists of values by condition.

As an example of this in the real world: on a large OFBiz project I worked on 
that finished last year we went into production with the entity cache turned 
OFF, completely DISABLED. Why? When doing load testing on a whim one of the 
guys decided to try it without the entity cache enabled, and the body of JMeter 
tests that exercised a few dozen of the most common user paths through the 
system actually ran FASTER. The database (MySQL in this case) was hit over the 
network, but responded quickly enough to make things work quite well for the 
various find queries, and FAR faster for updates, especially creates. This 
project was one of the higher volume projects I'm aware of for OFBiz, at peaks 
handling sustained processing of around 10 orders per second (36,000 per hour), 
with some short term peaks much higher, closer to 20-30 orders per second... 
and longer term peaks hitting over 200k orders in one day (north America only 
day time, around a 12 hour window).

I found this to be curious so looked into it a bit more and the main 
performance culprit was updates, ESPECIALLY creates on any entity that has an 
active list cache. Auto-clearing that cache requires running the condition for 
each cache entry on the record to see if it matches, and if it does then it is 
cleared. This could be made more efficient by expanding the reverse index 
concept to index all values of fields in conditions... though that would be 
fairly complex to implement because of the wide variety of conditions that CAN 
be performed on fields, and even moreso when they are combined with other 
logic... especially NOTs and ORs. This could potentially increase performance, 
but would again add yet more complexity and overhead. 

To turn this dilemma into a nightmare, consider caching view-entities. In 
general as systems scale if you ever have to iterate over stuff your 
performance is going to get hit REALLY hard compared to indexed and other less 
than n operations.

The main lesson from the story: caching, especially list caching, should ONLY 
be done in limited cases when the ratio of reads to write is VERY high, and 
more particularly the ratio of reads to creates. When considering whether to 
use a cache this should be considered carefully, because records are sometimes 
updated from places that developers are unaware, sometimes at surprising 
volumes. For example, it might seem great (and help a lot in dev and lower 
scale testing) to cache inventory information for viewing on a category screen, 
but always go to the DB to avoid stale data on a product detail screen and when 
adding to cart. The problem is that with high order volumes the inventory data 
is pretty much constantly being updated, so the caches are constantly... 
SLOWLY... being cleared as InventoryDetail records are created for reservations 
and issuances.

To turn this nightmare into a deal killer, consider multiple application 
servers and the need for either a (SLOW) distributed cache or (SLOW) 
distributed cache clearing. These have to go over the network anyway, so might 
as well go to the database!

In the case above where we decided to NOT use the entity cache at all the tests 
were run on one really beefy server showing that disabling the cache was 
faster. When we ran it in a cluster of just 2 servers with direct DCC (the best 
case scenario for a distributed cache) we not only saw a big performance hit, 
but also got various run-time errors from stale data.

I really don't how anyone could back the concept of caching all finds by 
default... you don't even have to imagine edge cases, just consider the 
problems ALREADY being faced with more limited caching and how often the entity 
cache simply isn't a good solution.

As for improving the entity caching in OFBiz, there are some concepts in Moqui 
that might be useful:

1. add a cache attribute to the entity definition with true, false, and never 
options; true and false being defaults that can be overridden by code, and 
never being an absolute (OFBiz does have this option IIRC); this would default 
to false, true being a useful setting for common things like Enumeration, 
StatusItem, etc, etc

2. add general support in the entity engine find methods for a "for update" 
parameter, and if true don't cache (and pass this on to the DB to lock the 
record(s) being queried), also making the value mutable

3. a write-through per-transaction cache; you can do some really cool stuff 
with this, avoiding most database hits during a transaction until the end when 
the changes are dumped to the DB; the Moqui implementation of this concept even 
looks for cached records that any find condition would require to get results 
and does the query in-memory, not having to go to the database at all... and 
for other queries augments the results with values in the cache

The whole concept of a write-through cache that is limited to the scope of a 
single transaction shows some of the issues you would run into even if trying 
to make the entity cache transactional. Especially with more complex finds it 
just falls apart. The current Moqui implementation handles quite a bit, but 
there are various things that I've run into testing it with real-world business 
services that are either a REAL pain to handle (so I haven't yet, but it is 
conceptually possible) or that I simply can't think of any good way to 
handle... and for those you simply can't use the write-through cache.

There are some notes in the code for this, and some code/comments to more 
thoroughly communicate this concept, in this class in Moqui:

https://github.com/moqui/moqui/blob/master/framework/src/main/groovy/org/moqui/impl/context/TransactionCache.groovy

I should also say that my motivation to handle every edge case even for this 
write-through cache is limited... yes there is room for improvement handling 
more scenarios, but how big will the performance increase ACTUALLY be for them? 
The efforts on this so far have been based on profiling results and making sure 
there is a significant difference (which there is for many services in Mantle 
Business Artifacts, though I haven't even come close to testing all of them 
this way).

The same concept would apply to a read-only entity cache... some things might 
be possible to support, but would NOT improve performance making them a moot 
point.

I don't know if I've written enough to convince everyone listening that even 
attempting a universal read-only entity cache is a useless idea... I'm sure 
some will still like the idea. If anyone gets into it and wants to try it out 
in their own branch of OFBiz, great... knock yourself out (probably 
literally...). But PLEASE no one ever commit something like this to the primary 
branch in the repo... not EVER.

The whole idea that the OFBiz entity cache has had more limited ability to 
handle different scenarios in the past than it does now is not an argument of 
any sort supporting the idea of taking the entity cache to the ultimate 
possible end... which theoretically isn't even that far from where it is now. 

To apply a more useful standard the arguments should be for a _useful_ 
objective, which means increasing performance. I guarantee an always used find 
cache will NOT increase performance, it will kill it dead and cause infinite 
concurrency headaches in the process.

-David




> On 19 Mar 2015, at 10:46, Adrian Crum <[email protected]> 
> wrote:
> 
> The translation to English is not good, but I think I understand what you are 
> saying.
> 
> The entity values in the cache MUST be immutable - because multiple threads 
> share the values. To do otherwise would require complicated synchronization 
> code in GenericValue (which would cause blocking and hurt performance).
> 
> When I first starting working on the entity cache issues, it appeared to me 
> that mutable entity values may have been in the original design (to enable a 
> write-through cache). That is my guess - I am not sure. At some time, the 
> entity values in the cache were made immutable, but the change was incomplete 
> - some cached entity values were immutable and others were not. That is one 
> of the things I fixed - I made sure ALL entity values coming from the cache 
> are immutable.
> 
> One way we can eliminate the additional complication of cloning immutable 
> entity values is to wrap the List in a custom Iterator implementation that 
> automatically clones elements as they are retrieved from the List. The 
> drawback is the performance hit - because you would be cloning values that 
> might not get modified. I think it is more efficient to clone an entity value 
> only when you intend to modify it.
> 
> Adrian Crum
> Sandglass Software
> www.sandglass-software.com
> 
> On 3/19/2015 4:19 PM, Nicolas Malin wrote:
>> Le 18/03/2015 13:16, Adrian Crum a écrit :
>>> If you code Delegator calls to avoid the cache, then there is no way
>>> for a sysadmin to configure the caching behavior - that bit of code
>>> will ALWAYS make a database call.
>>> 
>>> If you make all Delegator calls use the cache, then there is an
>>> additional complication that will add a bit more code: the
>>> GenericValue instances retrieved from the cache are immutable - if you
>>> want to modify them, then you will have to clone them. So, this
>>> approach can produce an additional line of code.
>> 
>> I don't see any logical reason why we need to keep a GenericValue came
>> from cache as immutable. In large vision, a developper give information
>> on cache or not only he want force the cache using during his process.
>> As OFBiz manage by default transaction, timezone, locale, auto-matching
>> or others.
>> The entity engine would be works with admin sys cache tuning.
>> 
>> As example delegator.find("Party", "partyId", partyId) use the default
>> parameter from cache.properties and after the store on a cached
>> GenericValue is a delegator's problem. I see a simple test like that :
>> if (genericValue came from cache) {
>>    if (value is already done) {
>>       getFromDataBase
>>       update Value
>>    }
>>    else refuse (or not I have a doubt :) )
>> }
>> store
>> 
>> 
>> Nicolas

Reply via email to