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
>> 
>> 

Reply via email to