Skip to content

HV-2127 Make Path implementation not rely on Lists (or other collections) #1672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

marko-bekhta
Copy link
Member

https://hibernate.atlassian.net/browse/HV-2127


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on licensing, please check here.


Copy link
Member Author

@marko-bekhta marko-bekhta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be looked at by commit. The general idea here was to drop the use of the lists in the path implementation and just build the "chain" from the nodes.
One "downside" of this is that we do not have an easy way to iterate the path from the "root" as we only have the leaf node. If we add the root to the path, it won't help much since we
have the one-way links to the parent. I think adding the link to the leaf node won't help as we could have multiple leaves... and otherwise... reconstructing the whole path from the root to create a new leaf would probably take us back to where we were with the list ...

Copy link
Member

@gsmet gsmet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One "downside" of this is that we do not have an easy way to iterate the path from the "root" as we only have the leaf node.

But do we need to? Or does the user need to?

@marko-bekhta
Copy link
Member Author

and some numbers:

main:

Benchmark                                                                                                          Mode  Cnt      Score    Error   Units
o.h.v.p.cascaded.CascadedWithLotsOfItemsAndMoreConstraintsValidation.testCascadedValidationWithLotsOfItems        thrpt   20   9210.215 ± 54.555   ops/s
o.h.v.p.simple.SimpleValidation.testSimpleBeanValidation                                                          thrpt   20  17446.229 ± 95.293  ops/ms

Benchmark                                                                                                          Mode  Cnt      Score    Error   Units
o.h.v.p.cascaded.CascadedWithLotsOfItemsAndMoreConstraintsValidation.testCascadedValidationWithLotsOfItems        thrpt   20   9447.360 ± 28.172   ops/s
o.h.v.p.simple.SimpleValidation.testSimpleBeanValidation                                                          thrpt   20  15886.786 ± 39.997  ops/ms


change:
Benchmark                                                                                                          Mode  Cnt      Score    Error   Units
o.h.v.p.cascaded.CascadedWithLotsOfItemsAndMoreConstraintsValidation.testCascadedValidationWithLotsOfItems        thrpt   20  13766.536 ± 43.834   ops/s
o.h.v.p.simple.SimpleValidation.testSimpleBeanValidation                                                          thrpt   20  26797.112 ± 80.112  ops/ms

Benchmark                                                                                                          Mode  Cnt      Score    Error   Units
o.h.v.p.cascaded.CascadedWithLotsOfItemsAndMoreConstraintsValidation.testCascadedValidationWithLotsOfItems        thrpt   20  14434.296 ± 67.744   ops/s
o.h.v.p.simple.SimpleValidation.testSimpleBeanValidation                                                          thrpt   20  23922.016 ± 89.195  ops/ms

@marko-bekhta
Copy link
Member Author

One "downside" of this is that we do not have an easy way to iterate the path from the "root" as we only have the leaf node.

But do we need to? Or does the user need to?

I hope not, and I hope others would just use the string representation of the path ... but it may be that some lib goes through the path to construct the "violation" .. I'm thinking about something like org.zalando:problem (https://github.com/zalando/problem) I'll take a look at it a bit later to see what's happening there 🙈

@marko-bekhta marko-bekhta force-pushed the feat/node-path-optimizations branch from 272bb90 to 25729f0 Compare August 7, 2025 13:45
@marko-bekhta
Copy link
Member Author

One "downside" of this is that we do not have an easy way to iterate the path from the "root" as we only have the leaf node.

But do we need to? Or does the user need to?

I hope not, and I hope others would just use the string representation of the path ... but it may be that some lib goes through the path to construct the "violation" .. I'm thinking about something like org.zalando:problem (https://github.com/zalando/problem) I'll take a look at it a bit later to see what's happening there 🙈

ok... I had a look at Quarkus and there are a few places where the path iterator is used to construct the response... e.g.:

https://github.com/quarkusio/quarkus/blob/ebee0a4b66d1b4dba9c51165e78d22fbf3b90d3e/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java#L59-L69

or here:

https://github.com/quarkusio/quarkus/blob/ebee0a4b66d1b4dba9c51165e78d22fbf3b90d3e/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/interceptor/AbstractMethodValidationInterceptor.java#L155-L161

I could add some HV-specific methods to address these use cases... would Quarkus be able to leverage those?

@gsmet
Copy link
Member

gsmet commented Aug 7, 2025

I could add some HV-specific methods to address these use cases... would Quarkus be able to leverage those?

I think I misunderstood you previously: I don't think you can break the contract of Path. It extends Iterable so iterator() should work.

Now the question is: can you somehow materialize the Path when we get a constraint violation? Because having a constraint violation is somehow a corner case. Most of the time, you shouldn't have one.

Copy link
Member

@gsmet gsmet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few comments.

FWIW, we could have a specific benchmark with only things that are valid and measure the improvements there.

Then have one where we have failures to measure that things have not gone too badly for this case.

@marko-bekhta marko-bekhta force-pushed the feat/node-path-optimizations branch from 77c3615 to 73937f4 Compare August 11, 2025 12:36
@gsmet gsmet changed the title HV-2127 Make Path implementaion not rely on Lists (or other collections) HV-2127 Make Path implementation not rely on Lists (or other collections) Aug 11, 2025
@gsmet
Copy link
Member

gsmet commented Aug 11, 2025

@marko-bekhta were you able to re-run the benchmarks with this new code?

@marko-bekhta
Copy link
Member Author

yes. it seems still better than what we currently have 🙂 here's what I saved from Friday:

main:
Benchmark                                                     Mode  Cnt      Score    Error   Units
SimpleSingleElementValidation.invalidObjectValidation        thrpt   20   6474.390 ± 31.799  ops/ms
SimpleSingleElementValidation.validObjectValidation          thrpt   20  17840.565 ± 51.801  ops/ms

this patch:
Benchmark                                                     Mode  Cnt      Score     Error   Units
SimpleSingleElementValidation.invalidObjectValidation        thrpt   20   8527.579 ±  40.861  ops/ms
SimpleSingleElementValidation.validObjectValidation          thrpt   20  33352.222 ± 402.655  ops/ms

or you mean just the few latest commits with the MaterializedPath ?

(btw ^ this is from a "new benchmark" I've added with one simple entity being either valid or invalid. I noticed that some of our other benchmarks that use random can have a quite different results from one run to the other) so I wanted to have something more "stable")

it seems still better than what we currently have

which does make sense since we do not create/copy the lists on each operation, but just create one array for the failing constraint.

@gsmet
Copy link
Member

gsmet commented Aug 11, 2025

(btw ^ this is from a "new benchmark" I've added with one simple entity being either valid or invalid. I noticed that some of our other benchmarks that use random can have a quite different results from one run to the other) so I wanted to have something more "stable")

Yeah, it makes perfect sense.

Looks all good to me if you want to merge it.

@marko-bekhta
Copy link
Member Author

Thanks for having a look at this one, Guillaume! 🙏🏻 🙇🏻 🙂
yeah.. I should probably merge it before switching to the bean tracking PR so that there would be less problems with rebasing... I'll have a look at what Sonar suggests to fix and then merge 🙂

@marko-bekhta marko-bekhta force-pushed the feat/node-path-optimizations branch from 73937f4 to bb2973c Compare August 11, 2025 14:38
Copy link

@marko-bekhta marko-bekhta marked this pull request as ready for review August 11, 2025 15:43
@marko-bekhta marko-bekhta merged commit 015a622 into hibernate:main Aug 11, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants