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
