Happy to provide the proof, would hardly have believed those numbers myself if 
I didn't see them up close and personal :).
Promise to pick an app that's a better benchmarking case next time!

Cheers,
- hugi



> On 15 Oct 2026, at 11:47, Andrus Adamchik <[email protected]> wrote:
> 
> Hi Hugi,
> 
> Sharing your chart with my team and clients to prove I am not making up those 
> performance gains :) 
> 
>> Already got a few list e-mails from you queued up that I want to reply to, 
>> just haven't had the time.
> 
> Looking forward!
> 
>> * BTW, not having to upgrade the model version manually is a lovely 
>> usability enhancement for testing like this.
> 
> I had hoped it will be a drop-in replacement. But then we still needed to 
> patch both Agrest and Bootique to try out the snapshot. At that rate of 
> change, hard to keep things aligned.
> 
> Andrus
> 
> 
> 
>> On Oct 15, 2026, at 6:51 AM, Hugi Thordarson <[email protected]> wrote:
>> 
>> 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