And there are more select optimizations. All the CPU time savings below are 
"the Cayenne part of a select query" :

* https://issues.apache.org/jira/browse/CAY-3049 Faster, lower-overhead LRU 
snapshot cache. Saves 40-60% CPU time on plain selects, 20-45% with prefetches.
* https://issues.apache.org/jira/browse/CAY-3050 [STILL IN PROGRESS, but almost 
ready] Faster. Saves 30-40% CPU time on plain selects, 20-40% with prefetches. 
25-30% less memory allocation.
* https://issues.apache.org/jira/browse/CAY-3051 somewhat lower memory 
allocation overhead

The numbers almost look too good to be true :) But good data structures go a 
long way. I will be checking this in a real application soon, and would love an 
independent confirmation once CAY-3050 makes it in.

Andrus

> On Oct 11, 2026, at 6:34 PM, Andrus Adamchik <[email protected]> wrote:
> 
> 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