Hi,

Just pushed a couple of big improvements to DataContext read concurrency. 
Related jiras: CAY-3044, CAY-3045, CAY-3046, CAY-3047, CAY-3048.

Consider a single DataContext used by many threads in parallel for selecting 
objects. I have a number of very active read-only apps, and an app-scoped 
DataContext has always been the default pattern, especially valuable with local 
cache. It works great until concurrency increases causing every single select 
to queue up on ObjectStore waiting to load its rows into the context. 

So there was a de-facto hard cap on context throughput. Previously, I solved 
this via context pooling. This is ugly and luckily is no longer needed in this 
release. In my benchmarks, throughput improved 2x. This number will vary 
depending on the connection pool, CPU, DB speed of course. But lock contention 
is gone and the new usage advice is:

1. If you DON'T use local cache, then a new DataContext per thread (= per 
request) is the most performant option.
2. If you DO use local cache (or want to keep such option open for the future; 
or don't want to deal with per-request scope), use a shared DataContext and 
don't worry about locking. This option is slightly (~5%) slower than a 
per-thread context, but this is negligible.

Before the release, I would appreciate if someone could try it out and report 
any issues. The changes touch on the object / snapshot consistency model. So if 
you see anything weird, like older versions of objects appearing after newer 
ones, please report. (To be clear, I don't expect this to happen :))


---

Finally, some high-level technical details. You can stop reading here :) 

* Instead of broad per-select lock to load objects, the locking is done per 
object, using private "striped" locks. I was surprised using "synchronized" in 
a loop (on different monitors) is not slow. But apparently it is fast with 
modern Java. 
* ObjectContext / ObjectStore object lookups are not synchronized anymore
* Eventual consistency of object states is ensured by per-object locks and 
incrementing snapshot versions. We had snap versions before, but now they are 
faster, smarter and actually help.
* Merge strategy fix to avoid excessive to-one faulting post-query. I was 
occasionally confused seeing objects fetched one-by-one right after they were 
fetched by another query prefetch. Thought this was due to weak references. 
Nope, just faulty merge logic + concurrency.
* Per-statement snapshot versions (instead of per-object)
* Safer fault checking to avoid a specific race condition causing 
ClassCastExceptions under load 


Andrus

Reply via email to