Hi Andrus,

I'm like a child at christmas with the Cayenne updates these days. Was planning 
on waiting for M4 before upgrading, but lost willpower and moved to -SNAPSHOT 
last night. Quick migration as usual (2 utility libs, 1 large-ish app).

I don't often share ObjectContexts between threads and I've done very little 
caching / performance tuning in the app I migrated so it's performance is more 
DB dependent than Cayenne dependent (few huge queries rather than many small 
queries), but I benchmarked the app before/after and lo and behold; The page I 
measured — the heaviest page in the app — still got a free 11% performance 
improvement! Seemingly mostly due to the optimized data structures/fetching and 
a somewhat enormous reduction in memory use :).

Asked Claude to do some benchmarking, comparing M3 and -SNAPSHOT for our core 
logic. The report is here, clearly shows the huge concurrency/memory wins: 
https://www.whoacommunity.com/cayenne-load.html .  (also shows I really need to 
start looking into caching in this app)

Since everything works great I've deployed -SNAPSHOT to production for testing 
(relatively high-traffic and lots of data, my own project so no one dies in 
case of errors).

Really looking forward to having the time to sit down and try everything else, 
lots of lovely things there! Already got a few list e-mails from you queued up 
that I want to reply to, just haven't had the time. *sigh* I'm not a great 
correspondent these days. Need more hours in the day.

In short, yet another awesome upgrade, so once again, thanks!

====

Notes from last night's upgrade process if anyone is thinking about trying:

* 2min : Organized imports, done (CAY-2826)
* 5min : Remove serialization from superclass templates and regenerate 
(CAY-3020)
* 10min : Accommodate renamed AST* classes and a few minor API changes (like 
the change to Entity.resolvePathComponents) (CAY-3039)
* 10min : Fix a couple of NullPointerExceptions, due to null values appearing 
with the changed join behaviour from CAY-3032. Worth it though, good change.
* 10min : CAY-3010, Turns out a couple of timestamp columns in my postgres DB 
were defined as timestamptz (timezoned) which the new logic understandably 
didn't want to map to a LocalDateTime. In my case the fix was easy, the columns 
weren't supposed to be timezoned (since I live in a developer's paradise of 
eternal UTC+00:00) so just changed the type and everything worked fine.
* BTW, not having to upgrade the model version manually is a lovely usability 
enhancement for testing like this.

====

Cheers,
- hugi



> On 14 Oct 2026, at 19:46, Andrus Adamchik <[email protected]> wrote:
> 
> 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