-
Notifications
You must be signed in to change notification settings - Fork 92
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
withTransaction does not keep the same transaction associated with the current Vert.x local context #1121
Comments
Tx propagation works if you stay on the same Vert.x local context. I'm not sure how you're implementing that in Spring boot, and I imagine you don't get it for free. |
@gavinking so that means that your comment on same Transaction per Vert.x context (the link I referred to above) is only relevant to Quarkus ? Can you elaborate more please, |
Well not just in Quarkus, in anywhere you have Vert.x driving the calls into your code. |
I still don't get it, how can I roll back a transaction when an error is thrown? It would not make sense to wrap 100 lines of code with the
|
@guy1699 it's really hard to discuss this stuff in the abstract, without knowing more about how your system works. It's made even harder by the fact that we've just woken up to the fact that for the last 4 months there's been a bug that rendered tx/session propagation pretty much completely broken (and so what you're experiencing could even just stem from that, I'm not sure). Basically, tx/session propagation works, by default, by storing the thing in the current Vert.x local context, meaning Vert.x is really the one responsible for propagating these things along the reactive stream. But this mechanism is completely pluggable: it's implemented by the (currently broken) Or, on the other hand, you always have the option of falling back to HTH at least a little. |
Thanks for the detailed answer @gavinking (not typical for you :)) So maybe the Can you share an example how to use it? |
I've just released HR |
Thanks, I'll check. |
Maybe the way we use it in the project can help?
And in case you need it, this is how you can access the Hibernate Reactive Vert.x context:
|
This is the PR that fixes the error @gavinking was talking about: #1118 |
Great @DavideD , I feel it's getting there.
|
Yes, it is working now, I tested it with the code below.
Still appreciate ideas on how to uni test without a real DB. |
Awesome!
We test everything using real database for several reasons:
In our project you can see several examples on how we unit tests. Line 66 in 748960c
This class is responsible for preparing a SessionFactory that the tests can use and makes sure that a container is started before running the testsuite (if docker is selected as an option). Line 85 in 748960c
No idea. |
There is also now an integration test added to the project to check for #1073: #1123 There are some caveats explained in the PR. In the future I want to improve the test but for now it should be good enough. I think that if you want to test this type of errors, you need to create a situation where you run different operations in parallel and then check that everything has been executed in the right order. In this paragraph of the documentation we touch briefly about the session and the contexts with an example of the kind of coding that doesn't work when using an hibernate session: I don't think there is an easy: http://hibernate.org/reactive/documentation/1.1/reference/html_single/#_sessions_and_vert_x_contexts By the way, our project is not so big, so if you want a working example, you should be able to clone the repository and run the tests from command line and the IDE and see working example of the tests in action. The README should contain enough infor to start (hopefully). |
Closing this issue because it's been solved by the latest release |
Hi, Could you please hand me how you get your "tenantService" instance? |
I think it's a service he created in his project. What are you trying to do? |
I am migrating an aplication from imperative to quarkus. The application is quite large, but the problem is localized when loading the demo data: [STEP 1] First I need to persist some entities of type A (about 1k in the current tests). [STEP 2] Then, the test pass these instances of A to other application (through REST), and gets some data. [STEP 3] The application need to update the instances of A with some of the information retrieved in the STEP 2. My problem is that, after creating the instances, the session is closed. So it is not possible to update the entities. I get HR000057. (If I run the updates in another worker Thread, I get the issue related with this post). So my question is, how can I keep the session open? I am using my own repository with the I think I should read a bit about vertx context, but any help/documentation is apreciated. |
I'm not sure how you can keep the session open (do you really want to do that?). But you can implement step 3 by merging the entities to the database. This will attach the entities to a new session and apply the changes. |
How would you update the entities with I will prefer to open and close the session on the first step and then open another one for the third step. I mean, it is like using two transactions:
But, all the steps are done in a single function call. |
But yes, it means opening two transactions. Can you show me some of the code? |
Maybe, I've misunderstood what you are trying to do. If you can show some code, I might have a better answer. |
Sure, here is the repository method: @Inject
Mutiny.SessionFactory sf;
public Uni<Party> create(final Party party) {
return sf.withTransaction((session, tx) -> session.persistAll(party,party.partyPersonalInfo))
.replaceWith(this.findByKeys(party.partyKey));
}
public Uni<Party> update(final Party party) {
return sf.withTransaction((session, tx) -> session.merge(party))
.replaceWith(this.findByKeys(party.partyKey));
} CreateDemoFile: ...
// Create the parties in the app BEGIN STEP 1
lstParty.forEach(element -> blParty.create(element).await().indefinitely());
// COMMIT STEP 1
// Create the parties in the KC as users from the given parties BEGIN STEP 2
Map<String, String> kcSummary = blKeycloak.createAllFromPartyDEMO(lstParty)
.await()
.indefinitely();
// END STEP 2 (Uses other application)
// Error summary
for (String usr : kcSummary.keySet()) {
LOGGER.warn("--> PARTY: {}, ERROR : {} .(skipping)", usr, kcSummary.get(usr));
}
// BEGIN STEP 3 (this method call update for each party)
// Enrich all the parties with the username, isUser and isEnable field from KC
lstParty = blKeycloak.listEnrichParties(TENANT).await().indefinitely();
// COMMIT STEP 3
... I use await() since this is a file to generate some mock data just for testing purposes, in production, the application does not block the threads. I need to create some users (parties), then register the user in Keycloak and finally update the users after keycloak retrieve me some information. PD: I have a bl (basic logic) layer, that translate an entity into a DTO, but |
Something like this wouldn't work? @Inject
Mutiny.SessionFactory factory:
...
// Open the session
factory.withSession( session -> session
// Step 1
.withTransaction( tx -> lstParty.forEach(element -> blParty.create(element) ) )
// Step 2
.map( unused -> blKeycloak.createAllFromPartyDEMO(lstParty) )
.invoke( kcSummary -> kscSummary.forEach( (key, value) -> LOGGER.warn("--> PARTY: {}, ERROR : {} .(skipping)", key, value) )
.chain( kcSummary -> session.withTransaction( tx -> {
// STEP 3: The session is still open
...
})
} ); Hibernate Reactive should be able to find the open session or transaction and reuse it in the repo. factory.withSession( session -> session
// Step 1
.withTransaction( tx -> lstParty.forEach(element -> blParty.create(session, element) ) )
... |
By the way, I explain different ways to test with Hibernate Reactive and Panache using We need to update the documentation on this subject. |
Thanks for the help @DavideD, just one last question. How can I pass a session through several methods? |
If we assume that the caller starts the transaction (like in my previous example), you can change public Uni<Party> create(Mutiny.Session session, final Party party) {
return session.persistAll(party,party.partyPersonalInfo)
.replaceWith(this.findByKeys(party.partyKey));
} otherwise: public Uni<Party> create(Mutiny.Session session, final Party party) {
return session.withTransaction( tx -> session
.persistAll(party,party.partyPersonalInfo).replaceWith(this.findByKeys(party.partyKey))
);
} It depends on how you want to design your app. |
Okay, yes I was wondering if it possible to keep the session open without chaining the operations. But as this is only for loading data to a test DB, I will try the Thanks again |
How would you make sure that things happen in the right order ( |
And, just as a reminder, nested calls to |
I solve the original issue about the (step 1 -> step 2 -> step 3) by chaining the operations, after that I get some troubles with other parts of the demo loader. Sorry, I didn't explain it properly This was one of the problems, I was running some of the methods on a worker thread, thus I get:
Now that I have remove the Yeah, I understand that concurrence can lead to race conditions (that will be translated into DB inconsistencies), but as this is a Demo. I just need to load some bulk (fake) data, so using the same session on parallel chains of operations may be useful to load non related entities at once. Anyway, thanks to your help, I have a clearest idea about how HR handle the sessions. TY! |
I'm counting on the documentation explaining one can work with the same reactive-stream and keep calling
withTransaction
to keep adding DB operations to the same transaction, but it does not work.https://hibernate.org/reactive/documentation/1.0/reference/html_single/#:~:text=When%20you%20create,see%20this%20error%3A
I'm running on Spring boot using
Uni
all the way.I'm testing it by inserting to the DB and then throwing an exception, expecting the insert will be rolled back.
I'm calling the endpoint below to test it and the exception being thrown while the new entity is stored in the DB:
reactiveSessionFactory
created by:The text was updated successfully, but these errors were encountered: