All work

Select view

Select search mode

 
50 of 90

Important performance degration on flush in Hibernate 6

Description

Hi,

I am migrating from Hibernate 5 to Hibernate 6 and noticed huge performance degradation during flush when there are many managed entities in the session (in some cases we went from 1mn of flush to 10mn of flush because of this).

It is caused by the change introduced in https://hibernate.atlassian.net/browse/HHH-6999 in org.hibernate.event.internal.AbstractFlushingEventListener#prepareEntityFlushes: https://github.com/hibernate/hibernate-orm/pull/8515/files#diff-b0bb2d87486d2105bfe77236eaef49bcf1d4dff9ad84491cf5101e976dd77136R164

Basically what happens is the following:

  • the second loop is calling Cascade.cascade on every entity currently in session

  • even though enhancedForLazyLoading is true, we end up setting dirtyAttributes to null

  • and then EACH and EVERY field of the entry are checked one by one to see if they changed

The code is too hard for me to understand to just propose a solution, but if anybody shares with me how this is meant to work and what a solution would look like, I can try contributing.

Also any workaround for this would be greatly appreciated :)

Attachments

2
  • 29 Apr 2025, 04:16 PM
  • 29 Apr 2025, 04:16 PM

Assignee

Reporter

Worked in

Components

Affects versions

Priority

Created 7 hours ago
Updated 40 minutes ago

Activity

Show:

Gavin King 40 minutes ago

the first one most of the time incurs no cost because it’s a noop but the second one will go through all entities managed or readonly (well after your patch above, it will just be managed entities).

OK, now I see the source of the issue. Easy fix, I believe.

Gavin King 1 hour ago

This will help for readonly entities I guess, but not for managed one?

Correct.

Victor Noël 2 hours ago
Edited

Thanks for the explanation, I agree I’m not going to rock the world with my 1h reading of the code ^^

Anyway, you can try this out and see if it helps: https://github.com/hibernate/hibernate-orm/pull/10096

This will help for readonly entities I guess, but not for managed one?

We have to read all fields of a managed entity at flush time anyway, because we have to dirty check them. So this could not possibly be responsible for a 10x slowdown

So just to be clear, I believe the slow down is caused by the addition of this code: https://github.com/hibernate/hibernate-orm/pull/8515/files#diff-b0bb2d87486d2105bfe77236eaef49bcf1d4dff9ad84491cf5101e976dd77136R164. In org.hibernate.event.internal.AbstractFlushingEventListener#prepareEntityFlushes there are two loops, the first one most of the time incurs no cost because it’s a noop but the second one will go through all entities managed or readonly (well after your patch above, it will just be managed entities). I am not saying this code is not correct of course, but that’s what is causing that slow down.

This is with Hibernate 5:

This is with Hibernate 6:

So as you can see in the second screenshot, not bytecode optimization happens as you mentioned, so I see an opportunity for me to enable something that is not currently enabled. Could you maybe tell me what should be done to enable this? Right now I am using the compile time bytecode enhancement (enableLazyInitialization=true, enableDirtyTracking=true but surprisingly enableAssociationManagement=false and not true…).

Disabling all of this would give us a good information then? That’s what I will try for now.

Is there anything else I could manually enable to get this “reflection optimizer”?

Gavin King 3 hours ago

Anyway, you can try this out and see if it helps: https://github.com/hibernate/hibernate-orm/pull/10096

Gavin King 3 hours ago

Also, I wanted to point out that when I did a bit of debugging, I noticed that, for MANAGED entities, the code in Cascade.cascade goes through each and every field, even those that couldn’t be cascaded at all I think and would fetch their values using reflection (which accounted for ~66% of the time spent in this method) for nothing in the end

I don’t know what code you’re referring to here (line number?) but I think you’re making a lot of assumptions here. A couple of comments:

  1. We have to read all fields of a managed entity at flush time anyway, because we have to dirty check them. So this could not possibly be responsible for a 10x slowdown.

  2. Reading a several fields (even by reflection) and putting their values in a perfectly pre-sized array is not usually a particularly expensive operation compared to everything else Hibernate has to do at flush-time, and in case it is, Hibernate can use a “reflection optimizer” which uses bytecode generation to eliminate the reflection. I’ve personally never once heard of anyone turning on the reflection optimizer and observing a significant increase in performance. (Not saying it’s impossible for that to happen in specific cases.)

  • would check all fields of entities even when using dirty checking bytecode enhancement

If I were you I would turn off bytecode enhancement and see what happens. Let me know if your program gets faster or slower.

Do you think there are opportunity for improving performance

I mean, y’know, maybe, but I never believe in any “performance bug” or “performance optimization” that arises from someone eyeballing code in the debugger. I don’t trust my own intuitions about performance, let alone anyone else’s. I want to see actual numbers.

Flag notifications