-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
feat: read only transactions #7455
Conversation
Thanks @loicmathieu ! Bear with me, my CDI fu isn't very strong - but I have the impression this would allow to use the same EntityManager instance before (and after) the read-only transaction? I don't think we should allow that, it gets very tricky to handle correctly - let me elaborate: This implies that objects can be attached to the EM during such read-only block, and be flagged to be treated as "read only", but after the block is ended (or before) people are allowed to add read-write objects - so you end up having a mixture of read-only and read-write managed objects within the same sesssion, this could get ugly and unpredictable. A practical example, is this failure to write changes:
You could have similar things happening, the opposite way:
It gets more tricky and more complicated when you consider object graphs, the fact that many of these managed entities will be relating to each other - and some will be loaded as side-effect so possibly ending up in the wrong "scope". In summary, let's make sure when entering the RO scope we're not reusing an existing RW EntityManager, and that we close it on exiting of such state. Of course it would be nice to actually share the same RO EntityManager across multiple RO blocks, so to leverage the automatic caching layer. |
122f952
to
27cffdb
Compare
@Sanne I check what Spring does and it set the session to readOnly at transaction begin and set back the session to the old state at the end of the transaction. The current implementation is not tied to a transaction because I didn't add the But even with the addition of the annotation, there can still be mixed RO/RW transactions as you describe because the transaction support is inside Narayana that is not aware of Hibernate. As transactions can be nested in Narayana we should ideally merge the two interceptors and set the session to readOnly inside I updated my current implementation to mandate a transaction (by checking that the target have the If I understand correctly the code inside If this is not enough to implements correctly read only transactions, we will need a deeper collaboration between Narayana and Hibernate and this will be out of my knowledge range :) |
thanks @loicmathieu , setting the top level transaction to RO seems very sensible to me.
yes, EntityManager and Session are 1:1. They are actually the same instance: one class implements both interfaces to save on allocations (we used to have a wrapper but it's gone now).
I'm not sure I understand this. You mean people could start a nested R/W transaction within a RO parent transaction? We can definitely ask the Narayana team to give us some extra integration magic. /cc @ochaloup , you might want to be aware of this feature and maybe check we're not planning anything dumb? :) |
Normally not thanks to the trick I implemented to check that the And yes, if someone from Narayana can have a look and validate the implementation or propose a better one this could be good :) |
hi @loicmathieu and @Sanne, sorry for a bit delayed answering time. I checked the Spring issue where they provide the similar change spring-projects/spring-framework#21494 and from that perspective the way and the reasoning sounds good. First, let me recap of how I understand the change. The user now gets a way how to declare the method to work in "read-only" mode. This affects only the JPA access (no changes when the JDBC datasource is used). This makes the user responsible to determine the read-only method beforehand and answers for cases when the code (e.g. a third party one which is invoked at the scope of the started transaction) makes some writing operations eventually. Such a failure is then the responsibility of the user. If I understand right the way how the Regarding the implementation. As the interceptor makes changes in the JPA related behaviour it seems to me being more logical to be part of the hibernate/orm codebase than the transaction interceptors directly (btw. would not cause having this handling being part of the A minor questions
One major doubt is about what happens on the following case: |
In cas of transactions, we have one
I try this, but interceptor bindings are OR and not AND, so if I add two annotations, the interceptor is called when one of the two is used and I want it to be called when the two are used.
Will have a look.
Yes, this is why it's still a draft PR :)
This was also the concern of @Sanne . I definitly must add test about this, I don't know if a new entity manager will be created or not in this scenario. The read-only scope should be the one of the transaction, so starting a new transaction should means defining a new readOnly scope. Thanks for your feedback. |
@loicmathieu good we can then discuss the progress. I wonder about one think. You say
It seems to be pretty strange behaviour. I created a small reproducer and it seems it behaves as you describe. I need to check this with someone knowledgeable. |
Just for info, that strange behaviour of the interceptor binding is probably an issue. It may be followed at #7931 |
27cffdb
to
6dc84b1
Compare
@ochaloup now that #7931 is merged I update the code to use both interceptor binding annotations. I also add a test with nested I will need some help to understand why in case of |
@loicmathieu you mean the |
@ochaloup no, both interceptors are correctly called. |
@loicmathieu I'm checking the PR (albeit late), I see no doc and that makes me cry on the inside. Do you think we could have a paragraph explaining when and how to use such feature. Otherwise it iwll be limited to the 5 folks that followed that issue ;) |
@emmanuelbernard don't cry! This is a draft PR that why there is no doc. For the moment, it is not working properly, as soon as I find a way to implement it properly I will add documentation to it and make it ready for review. |
@loicmathieu I guess you still have to find a way to implement it properly? Thanks. |
@famod yes, I struggeled with the case of |
@loicmathieu I debugged the failing test So it seems there is an interceptor binding issue. |
Looking at https://github.com/quarkusio/quarkus/blob/master/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java and all those But instead of binding to Set<Annotation> bindings = InterceptorBindings.getInterceptorBindings(ic);
for (Annotation i : bindings) {
if (i.annotationType() == Transactional.class) {
return (Transactional) i;
}
} Btw, since you added |
That's strange: For testing purposes I removed |
It seems something goes wrong on this line: Line 90 in 61d80b8
value ist basically readOnly = true and the result of interceptorBinding.valueWithDefault(...) is readOnly = false , which matches my expectations at this point.
But then in return name.equals(that.name); So it is basically only comparing cc @mkouba @stuartwdouglas WDYT? Btw, for this debugging session I rebased this branch onto |
I don't think you can call it a read only transaction when it is only the EntityManager that is read only, any other transactional resources will work as normal. To me a read only TX implies that the interceptor will roll back the transaction instead of attempting to commit. IMHO this should be implemented in the TX interceptors, but then exposed as an attribute in the TSR. Hibernate should then be able to determine that the TX is read only when it creates the EM, and set it to read only that way. |
@famod Hm, each |
@stuartwdouglas I was also thinking of something like this: putting the transaction configuration inside the TSR and using it at EM creation time. Maybe it'll be a better implementation. |
@mkouba you desribe the issue to be only for boolean values right? As a workardoun to test my prototype I can switch to String values and it should works as expected ? |
@famod the design with multiple bindings is what we want to do, only trigger this intereptor if the As you discover some interceptor bindig implementation issue, let's wait for it to be fixed to check if the proposed implementation is correct or not. |
Ok, then you'll probably have to implement at least two interceptors ( FWIW, one alternative to that could be to have the existing tx-interceptors add something to |
Closing it in favor of #10077 |
Fixes #6414
@FroMage @Sanne this is a prototype of read only transaction support.
It uses a CDI interceptor inside the Hibernate extension to set some optimization at the session level.
Please advise if it is the right way to do this.
I don't know if adding a CDI interceptor is OK, I don't know the tradeoff (preformance cost ?)