The first wave of changes was for concurrent access to a single context only, the second - for any context (concurrent or not). Concurrency improvements made shared contexts 90-95% as fast as non-shared ones. Other changes made every context ~2x as fast either by making processing more efficient or allocating less memory or, often, both. The big ones are:
1. Bespoke array-based DataRows (fast value reads, no HashMap allocation) 2. Bespoke snapshot cache (much faster, smaller, cheaper to manage LRU) Also, I just finished this round (for now; there's always more stuff we can improve, like entity-specific generated DataRows with precompiled object accessors, etc.). Now in my webapps, Cayenne portion of the request fell below that of JDBC, for the first time ever. Andrus > On Oct 14, 2026, at 3:05 PM, John Huss <[email protected]> wrote: > > Are those optimizations for all ObjectContexts, not just the app-scoped > single context used by many threads that you mentioned? > > On Mon, Oct 12, 2026 at 7:15 PM Andrus Adamchik <[email protected]> wrote: > >> 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 >> >>
