I am maintaining code which looks like that
#Asynchronous
#TransactionTimeout(value = 1, unit = TimeUnit.HOUR)
public void downloadFile(Long fileId) {
//This method takes more than 1hour
service.download(fileId)
//this method should be called even when download finished with error
service.fileDownloadedFinishedNotification(fileId);
}
This is just an example code, to the fileDownloadedFinished we are passing message which we want to display etc, and inside of that we want to mark process as finished with error/success.
So as you can see on download we can get timeout, and after that the fileDownloadedFinishedNotification wont be called, because transaction failed because of the timeout.
I was thinking about extracting notification to other method and call it like this:
#Asynchronous
#TransactionTimeout(value = 1, unit = TimeUnit.HOUR)
public Future<String> downloadFile(Long fileId) {
//This method takes more than 1hour
service.download(fileId)
return new AsyncResult<String>("Test");
}
public void example(){
long id = 15;
String msg = "default stuff";
try {
msg = downloadFile(id).get();
}
catch (Exception e) {
e.printStackTrace();
}
service.fileDownloadedFinishedNotification(fileId, string);
}
But I am not sure if it is good idea, or maybe there is some other functionality, which I can call when timeout is reaced. Something like onTimeout.
Some considerations :
There is no simple way to handle transaction timeout with a listener AFAIK
Annotations use dynamic proxies under the cover, they won't so be applied on a inner call, you have to call your downloadFile from outside (on a bean injected in your caller).
The current transaction will be aborted when fileDownloadedFinishedNotification will be called and so all operations on a transacted resource (DB, etc...) will be rolled back (you may have to invoke the method within a dedicated transaction (e.g. annotate your method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW).
Assuming the download method retrieves the content across the network and unless you access this last through a dedicated JCA adapter, no exception will be thrown on transaction timeout, the Transaction reaper only marks the current transaction as aborted and release related resources but does not interrupt the thread, only a subsequent access to a MANAGED resource (Datasource, JMS, etc) will throw an exception.
Regarding the last point, while interacting with a un-managed resource the only way to know if the current transaction is still active is to regularly check its state using EJBContext.getRollbackOnly() or by making a dummy access to any managed resource.
There are ways of achieving what you want but a proper implementation would need more information about your level of access to application change.
There are many places where transaction propagation is explained but giving you are running your app in an EJB containeer I would start from here:
https://docs.oracle.com/javaee/6/tutorial/doc/bncih.html
I would read it all chapter but most specific for your case is the container managed transactions here:
https://docs.oracle.com/javaee/6/tutorial/doc/bncij.html
Now assuming you have full access and you can change your database structure the way I would implemented this would be:
You are running your service in a parent transaction T1
Before you invoking the download method call another service to record the download started and the maximum expected time to finish. Do this in a REQUIRED_NEW transaction. This no time consuming database interaction will run in an autonomous transaction T2
Once the above T2 transaction commits your download started record is committed and available to query
Once back in the parent T1 start your download
Record the success in the same record you persisted in T2 if the download successfully finishes.
If you get a timeout the above will never be recorded and the database will still show the download as started and maximum expected time to finish
Define a monitoring process that would kick off at regular times and check the download status. If the expected time to finish have been passed over have that monitoring process alert or record failure or trigger another retry or whatever your business rules are
Hope it helped. Sorry for not codding examples but I thing you will have enough to start with.
Cheers
Related
I am implementing a backend service with Spring Boot. This service receives a REST request and executes some database operations and finally updates the status of the record.
After that, I would like to start a new async process and execute another data manipulation on the same record this way:
#Service
public class ClassA {
#Autowired
private ClassB classB;
#Autowired
private MyEntityRepository repo;
#Transactional
public void doSomething(Long id) {
// executing the business logic
if (isOk()) {
repo.updateStatus(id, Status.VERIFIED)
}
// I need to commit this DB transaction and return.
// But after this transaction is committed, I need
// to start an async process that must work on the
// same record that was updated before.
classB.complete(id);
}
}
And this is my async method:
#Service
public class ClassB {
#Autowired
private MyEntityRepository repo;
#Async
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void complete(Long id) {
Optional<MyEntity> myEntity = repo.findById(id);
if (myEntity.isPresent() && myEntity.get().getStatus == Status.VERIFIED) {
// execute 'business logic B'
}
}
}
The classA.doSomething() is called multiply times with the same id but business logic B must be executed only when the record status in the DB is VERIFIED.
The above solution works fine.
But my concern is the following: My test database is small and the classA.doSomething() method always finishes and closes its transaction BEFORE the classB.complete() starts to check the status of the same record in the DB. I see in the log that the SQLs are executed in the proper order:
* UPDATE STATUS FROM TABLE ... WHERE ID = 1 // doSomething()
* COMMIT
* SELECT * FROM TABLE WHERE ID = 1 // complete()
But is that 100% guaranteed that the 1st, classA.doSomething() method will always finish and commit the transaction before the 2nd classB.complete() async call check the status of the same record?
If the async method classB.complete() will be executed before classA.doSomething() finishes and execute its DB commit then I will break the business logic and the business logic B will be skipped (the new DB transaction will not see the updated status yet) and that will cause a big issue. Maybe this can happen if the database is huge and the commit takes longer than it takes in my small test DB.
Maybe I can operate with the DB transaction isolation levels described here but changing this can cause another issue in another part of the app.
What is the best way to implement this logic properly which guarantees the proper execution order with the async method?
It is NOT GUARANTEED that "the 1st, classA.doSomething() method will always finish and commit the transaction before the 2nd classB.complete() async call check the status of the same record".
Transactions are implemented as some kind of interceptors appropriate for the framework (this is true for CDI too). The method marked #Transactional is intercepted by the framework, so the transaction will not end before the closing } of the method. As a matter of fact, if the transaction was started by another method higher in the stack, it will end even later.
So, ClassB has plenty of time to run and see inconsistent state.
I would place the 1st part of doSomething in a separate REQUIRES_NEW transaction method (you may need to place it in a different class, depending on how you configured transaction interceptors; if you are using AOP, Spring may be able to intercept calls to methods of the same object, otherwise it relies on the injected proxy object to do the interception and calling a method through this will not activate the interceptor; again this is true for other frameworks as well, like CDI and EJB). The method doSomething calls the 1st part method, which finishes in a new transaction, then ClassB can continue asynchronously.
Now, in that case (as correctly pointed out in the comment), there is a chance that the 1st transaction succeeds and the 2nd fails. If this is the case, you will have to put logic in the system about how to compensate for this inconsistent state. Frameworks cannot deal with it because there is not one recipe, it is a per case "treatment". Some thoughts, in case they help: make sure that the state of the system after the 1st transaction clearly says that the second transaction should complete "shortly after". E.g. keep a "1st tx committed at" field; a scheduled task can check this timestamp and take action if it is too far in the past. JMS gives you all this - you get retries and a dead letter queue for the failed cases.
I have a Spring Rest Controller endpoint:
#PostMapping("/someheavyjob")
public Response indexAllReports() {
someService.startLongRunningOperation();
return Response.ok().build();
}
and then in the service class I have a method which performs many computations (databse read, other API calls etc.) :
public void startLongRunningOperation(){
List<String> involvedIds = otherApi.getAllActiveMembersIds(..);
involvedIds.forEach(id -> {
anotherComputatioMethod();
});
}
I know that this approach is bloking user's request until complete the job. I can solve it, it's in that state just to make it clear.
Quesiton: What I am considering about it is:
There should be only one instance of this heavy method running at a time (method: startLongRunningOperation).
Right now, as this method can be invoked from Rest controller, each api call will start this heavy method in new thread. There are mechanisms to rate limit users requests in java (eg. bucket4j), but wanted to ask you guys, what is the best way to handle this case ? Just one instance of long running task fired from Rest API calling Spring Service (which is and should be stateless) .
Edit 1:
To make it clear - blocking: I mean user have to wait for response unit whole task is finished - it can take minutes.
What I want to achieve is not to make this service method synchronized, but when the request comes, and in that time this long running task is working, then reject that 2nd request.
I am trying to call a method asynchronously with the help of completeableFuture in java
Like below:
#Transactional(propagation=Propagation.Required,readOnly=false)
public boolean updateStatusandMove(){
//update value in database
//move file via rest service
}
Now , this method is called asynchronously , with multiple completeable futures.
Some completaable future fails and some succeeds depending on availability of folder while moving file. For the ones who fails due to moving file httpserver exception is thrown, database values which were updated before moving should be rolled back, but it doesn't happen. So in short transactional rollback does not happen in completeable future. but if this method synchronously without completeable future , then rollback happens. Transactional annotation is used and it is spring boot application.
ExecutorsService service=Executors.newFixedThreadpool(50);
for(int i=0;i<10;i++)
{
Callable<CompletebleFuture<Boolean>> task=()->getcompletefuture(requestValues) //values from request will be passed
Future<CompleteableFuture<Boolean>> ftr=service.submit(task);
}
public CompleteableFuture<Boolean> getcompletefuture(requestValues){
Boolean ac=updateStatusandMove();
return CompleteableFuture.completedFuture(ac);
}
#Transactional(propagation=Propagation.Required,readOnly=false)
public boolean updateStatusandMove(){
//update value in database
//move file via rest service
}
I have a project running on Spring Boot 1.3.8, Hikari CP 2.6.1 and Hibernate (Spring ORM 4.2.8). The code on service layer looks like this:
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Hikari configuration has this: spring.datasource.leakDetectionThreshold=2000.
The problem is that external REST service is quite slow and often takes 2+ seconds to respond, as a result we see a lot of java.lang.Exception: Apparent connection leak detected which are nothing else but false negatives, though the problem can be clearly seen: we hold DB connection for the time we executing rest request.
The question would be: how to properly decouple DB and REST stuff? Or how to tell hibernate to release connection in between? So that we return DB connection to pool while waiting for REST response.
I have tried setting hibernate.connection.release_mode=AFTER_TRANSACTION and it kind of helps, at least we do not have connection leak exceptions. The only problem is that our tests started showing this:
2018-04-17 15:48:03.438 WARN 94029 --- [ main] o.s.orm.jpa.vendor.HibernateJpaDialect : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`
The tests are using injected DAO to insert records in DB and later check them via application API. They are not annotated with #Transactional and the list of listeners looks like this:
#TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class
})
Any ideas what could be the problem with tests?
In the code
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
I see three tasks here - fetching entity A, connecting to remote service and updating entity A. And all these are in same transaction, so the underlying connection will be held till the method is complete.
So the idea is to split the tasks one and three into separate transactions, there by allowing the connection to be releases before making the call to remote service.
Basically, with spring boot you need to add spring.jpa.open-in-view=false. This will not register OpenEntityManagerInViewInterceptor and thus entityManager (in-turn connection) is not bound to the current thread/request.
Subsequently, split the three tasks into separate methods with #Transactional. This helps us bind the entityManager to the transaction scope and releasing connection at end of transaction method.
NOTE: And do ensure that there isn't any transaction started/in progress before (i.e., caller - like Controller etc) calling these methods. Else the purpose is defeated and these new #Transactional methods will run in the same transaction as before.
So the high-level approach could look like below:
In spring boot application.properties add property spring.jpa.open-in-view=false.
Next you need to split doStuff method into three methods in new service class. Intent is to ensure they use different transactions.
First method with #Transactionalwill call A a = dao.findByWhatever();`.
Second method makes remote call.
Third method with #Transactionalwill call rest of the code with JPA merge or hibernate saveOrUpdate on objecta`.
Now Autowired this new service in your current code and call the 3 methods.
I'm having a strange issue.
In a class I have:
private final ScheduledExecutorService executor
= Executors.newSingleThreadScheduledExecutor();
public MyClass(final MyService service) {
executor.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
service.foo();
}
}, 0, 30, TimeUnit.SECONDS);
}
MyService is a spring bean that has #Transactional on its foo method. MyClass is instantiated only once (effectively singleton in the application)
After the first invocation of service.foo() (which works fine), on subsequent requests to the application I am randomly getting:
java.lang.IllegalStateException: Already value [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])] for key [org.hibernate.impl.SessionFactoryImpl#2cd91000] bound to thread [http-bio-8080-exec-10]
A few observations:
when the exception is thrown, the session stored in the TransactionSynchronizationManager is closed
the transaction synchronization manager resource map for the manually scheduled thread is empty
the exception occurs in http-bio-8080-exec threads, but the manually scheduled one is a pool- thread - so there is no 'thread polution'
MyClass is instantiated on startup, in a thread named "Thread-5", i.e. it is not in any way related to the http-bio threads.
If I comment the invocation to service.foo(), or get rid of the #Transactioanl annotation, everything works (except, of course, that data is not inserted in the db)
Any clues what the issue might be?
(Note: I prefer not to use #Scheduled - I don't want MyClass to be a spring bean, and the runnable has to operate on some of its internal state before invoking the service)
Update: After a while I'm able to reproduce it even without the scheduling stuff. So probably a general spring problem with the latest snapshot I'm using.
I assume that exception comes from an invocation of the TransactionInterceptor or the like (some Spring infrastructure bean), or are you using the TransactionSynchronizationManager from your own code somewhere? It appears to me that something is binding sessions to a thread being managed by your container (is that Tomcat 7?) and failing to unbind them before they're returned to the container's thread pool. Thus when the same thread is used for another transactional request later, Spring can't bind the new Session to it because the old one wasn't cleaned up.
I don't actually see anything to make me think it's directly related to your custom scheduling with MyClass. Are you sure it's not just a coincidence that you didn't see the exception when you remove the service.foo() call?
If you could catch one of those threads in a debugger when it's being returned to the pool with a Session still bound to it, you might be able to backtrack to what it was used for. An omniscient debugger would theoretically be perfect for this, though I've never used one myself: ODB and TOD are the two I know of.
Edit: An easier way to find the offending threads: add a Filter (servlet filter, that is) to your app that runs "around" everything else. After chain.doFilter(), as the last act of handling a request before it leaves your application, check the value of TransactionSynchronizationManager.getResourceMap(). It should be an empty map when you're done handling a request. When you find one that isn't, that's where you need to backtrack from to see what happened.