Here consistent request in Mongo I found how to handle consistent request in java code.
DB db...;
db.requestStart();
try {
db.requestEnsureConnection();
code....
} finally {
db.requestDone();
}
Does spring-data cares about it. Or I should do it manually as using plain java driver?
In spring data to execute with in same connection, you should use mongoTemplate.executeInSession(DbCallback action). Please take a look at the Spring documentation Section 5.1.1 about Execution Callback.
Related
Having a circuit breaker with Spring Boot for external calls (e.g. HTTP) is a common pattern that is rather easy to put into place, for example with resilience4j.
I cannot find any information about doing the same with database calls, via resilience4j or some other common pattern, and this is unexpected to me.
Suppose we have a service with a simple JDBC connection to a traditional SQL database. If the database goes down for any reason, I would like to be able to stop all incoming requests to the service at the controller level until the connection is restored. Is there a way to achieve what is essentially circuit breaker functionality for all the transactions happening over the connection to the database?
Yes, this is possible. Chapter 7 of the book Spring Microservices in Action gives an example of this. Effectively, you treat a SQL call in the same way as an HTTP call:
#CircuitBreaker(name = "entityService") ❶
public List<Entity> getEntity(String entityId) {
return entityRepository.findByEntityId(entityId);
}
You will need the following dependencies:
io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-circuitbreaker
io.github.resilience4j:resilience4j-timelimiter
org.springframework.boot:spring-boot-starter-aop
This is using an AOP pattern of adding new behavior to existing code without modifying the code itself.
In the standard synchronous Spring (WebMVC) world, there are OpenEntityManagerInViewFilter and OpenEntityManagerInViewInterceptor that hold a JPA EntityManager open during the whole processing of a request and allow to avoid the annoying LazyInitializationException ('no Session').
OpenEntityManagerInViewInterceptor also works in a similar way for async servlet API-based applications.
But how do you deal with this problem in a Webflux application? Let's say I have a Webflux controller that does something like
service.getOneReactively(...).flatMapMany(one -> obtainAFlux(one))
where service.getOneReactively() finds a domain object in the database and obtainAFlux() accesses a lazy collection on that domain object causing its load. Both calls will be executed on some thread pool, probably on different threads, so Hibernate's Session bound to the first thread (where the domain object is materialized) will not be accessible in the second thread (where the collection is loaded).
This results in LazyInitializationException.
How do you solve such a problem? The only thing I could invent so far is to pack both calls into a method and call it in a transaction in one reactive call.
Hibernate is not reactive, it uses JDBC which is a blocking database driver, and it in turn uses threadlocal to store session information which is not possible to use in Webflux.
If you wish to do blocking database calls you must use Mono.fromCallable and assign the calls to its own Scheduler so that it gets its own dedicate thread. You can read more about it here in the documentation https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking hibernate cant natively return a Flux.
Your code should look something like this
Flux<Objects> myObjects = Mono.fromCallable(() -> {
// make all your calls to the database here
// build then your flux or whatever you want
return Flux.fromIterable(listOfObjects);
}).subscribeOn(Schedulers.elastic()); // Makes sure this is run in its own thread
as #Toerktumlare mentioned, Hibernate is not reactive, it uses JDBC which is a blocking database driver, but you can use Hibernate reactive and use non-blocking methods and sessions.
a simple code example must be like this:
public Flux<MyUser> findAll() {
try {
Mutiny.SessionFactory factory = CustomEntityManagerFactoryPostgresImpl
.getInstance()
.getEntityManagerFactory()
.unwrap(Mutiny.SessionFactory.class);
CriteriaBuilder builder = factory.getCriteriaBuilder();
CriteriaQuery<MyUser> criteria = builder.createQuery(MyUser.class);
Root<MyUser> root = criteria.from(MyUser.class);
Flux<MyUser> myUser = Flux.fromIterable(
factory.withSession(
session -> session
.createQuery(criteria)
.getResultList()
).await().indefinitely());
factory.close();
return myUser;
} catch (Exception exception) {
log.error(String.valueOf(exception));
return Flux.empty();
}
and a mono<> is like:
public Mono<MyUser> getOneUser(Long id) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-persistence");
Mutiny.SessionFactory sessionFactory = emf.unwrap(Mutiny.SessionFactory.class);
Mutiny.Session session = sessionFactory.openSession();
try {
return Mono.fromFuture(session.find(MyUser.class, id).subscribe().asCompletionStage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Using blocking JPA API in a WebFlux application should be avoided.
If you want to access the RDBMS in a Spring WebFlux application, try to use the reactive streams API(none-blocking) instead.
Use Spring R2dbc/Spring Data R2dbc instead, see my Spring R2dbc example.
Use Hibernate Reactive, currently, there is no official Spring/Hibernate Reactive integration, but it is easy to integrate them.
Create a persistence.xml file.
Create a Mutiny.SessionFactory bean.
Use Mutiny.SessionFactory to handle RDBMS operations,see the PostRepository example written in Hiberante Reactvie.
The complete Spring/Hibernate reactive example can be found on my Github (but the web handling is using Vertx).
Mixed blocking JPA in a Spring Webflux application should be avoided. But it is possible, see my example which mixed JPA and R2dbc in a single application.
And the Open session in view pattern is desginated to work in a traditional servlet container and depends on Spring WebMVC.
I'm having a problem where I need to perform several slow HTTP requests on a separate thread after having written to the database using a JpaRepository. The problem is that doActualJob() blocks while waiting for a series of futures to resolve. This seems to prevent the underlying Hibernate session from closing, causing the application to run out of connections shortly after.
How do I write this function so the database connection isn't kept open while doing the blocking I/O? Is it even possible using JpaRepositories, or do I need to use a lower level API like EntityManager/SessionFactory?
#Service
class SomeJobRunner {
private final SomeJobRepository mSomeJobRepository; //extends JpaRepository
#AutoWired
public SomeJobRunner(final SomeJobRepository someJobRepository) {
mSomeJobRepository = someJobRepository;
}
#Async
public void doSlowJob(final long someJobId) {
SomeJob someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobStarted(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
doActualjob(); // Synchronous job doing several requests using Unirest in series
someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobEnded(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
}
Well - non-blocking database IO is not possible in Java/JDBC world in a standard way .To put it simply - your Spring data repository would be eventually using JPA ORM Implementation ( likes of Hibernate) which in turn will use JDBC to interact with the database which is essentially blocking in nature. There is work being done on this currently by Oracle (Asynchronous Database Access API ) to provide a similar API as JDBC but non-blocking. They intend to propose this as a standard. Also there is an exciting and parallel effort by Spring guys on this namely R2DBC – Reactive Relational Database Connectivity. They have actually integrated this with Spring data as well (link) so that may help you integrate in your solution. A good tutorial by Spring on this can be found here.
EDIT: As of 2022 Hibernate has reactive option as well
I would suggest to write in the database using a separate JTA transaction. Do do so, define a methode like
#Transactional(Transactional.TxType.REQUIRES_NEW)
public void saveJobStart(final long someJobId) {
SomeJob someJob = mSomeJobRepository.findOne(someJobId);
someJob.setJobStarted(Instant.now());
mSomeJobRepository.saveAndFlush(someJob);
}
Of course it is not quite the same. If doActualjob() fails, in your case, the database won't persist the start date. In my proposal, it will persist it. To compensate, you need to remove the start date in a catch bloc in doSlowJob, within a new transaction, and then rethrow the exception.
I want to update the DB and a server with some new data in one transaction.
I'm using java 8 and i was looking for Java transaction API.
I have found JTA but saw no code example. Can someone please link to one?
I saw this post and post
but there were about DB transaction and had no code example.
I want to make the transaction at a higher lever than the DAL level
as it wraps the peer-server update as well.
private void updateDbAndServer() throws Exception {
if (rulesUiRepository.updateRulesUiSnapshot(nonSplittedRulesSnapshot) == -1)
throw new RuntimeException("cannot save ui snapshot in DB");
Map<RuleConditionBl, RtRule> splittedMap = nonSplittedRulesSnapshot.toSplittedMap();
anotherService.updateConfig(splittedMap);
}
I think the Spring Transaction API is very easy to use and allows you to concentrate about your businesses logic. The API is used in a declarative form. Check this for more info.
I am using mongodb with spring mvc for a REST API. I have a controller which adds comments for events. This controller use a method of my event service. And implementation of them are below. However It seems to me as if my mongodb connection works without transaction. I have 1 insert and 1 update in that my controller adding comment. If update of event throws an error, the request commits inserting.Why is it not doing rollback? How can I fix this problem?
public Comments addComment(Comments comment) throws Exception{
comment.setCommentDate(SowUtil.getDateTimeDB());
comment.setCommenter(personService.findPersonById("531cc90e3c37b20bef47dfc7"));
mongoTemplate.insert(comment);
Events adv = findEventById(comment.getEventId());
adv.addComment(comment);
eventService.save(adv);
return comment;
}
MongoDB does not have the same notion of transactions as RDBMS. Based on your example, it looks like you are using two documents.
MongoDB has an article on 2-phase commit patterns involving multiple documents (
http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/). Perhaps, that can help you implementing a rollback?
I believe that Spring also has a notion of transaction support for MongoDB (http://docs.spring.io/spring-integration/reference/html/mongodb.html).