I am trying to define an Async method using Spring that needs to be called at the end of a synchronous function, like below:
void syncFunction() {
...
asyncFuntion();
}
#Async
void asyncFunction() {
...
}
I need to add a delay of 10 seconds before the execution of asyncFunction begins and would like to avoid adding Thread.sleep(). I've also explored #Scheduled annotation, but it doesn't seem to work for a one-time scheduled task. What is the best way to achieve this using Spring? Thanks in advance.
Related
I have a project on Spring boot and tried to use #Transactional and coroutines and got an error
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException.
Is it possible to use #Transactional and coroutines now?
override suspend fun invoke() {
val result = withContext(Dispatchers.IO) { deactivate() }
}
#Transactional
private suspend fun deactivate(): Int {
//data base call 1
//data base call 2
// ...
}
You can't start coroutines that do transactional work on a different thread during a #Transactional method call.
#Transactional leads to code being intercepted. Before and after deactivate(), the transaction-related code will be called for you.
Because the coroutines could be launched on a different thread, they will be launched outside of these transaction calls.
Relevant StackOverflow topic: Spring - #Transactional - What happens in background?
The reason to wrap a method in a transaction is to ensure that the method is atomic at a database level, and when there is a failure the whole transaction is reverted as if it never happened.
By breaking out of the transaction, using coroutines, you break the atomicity of the method if you do any other database actions, and when there is a failure those actions of the coroutine will not be reverted.
I hope you will be careful when using this (anti-) pattern!
I've got an Async method which is scheduled to run once a day:
#Component
public class MyClass {
//myCronProperty set to "0 25 20 * * *" in application.properties
#Scheduled(cron="{myCronProperty}")
#Async
#Override
public void doDailyTask() {
//Do work here
}
}
Is there a way of triggering doDailyTask() for testing purposes when the application is already running, perhaps by doing something clever with Groovy and reflection?
I figure I can always tweak the cron property to 1 minute in the future in my application.properties file, and then restart the application - but just wondered if there was a smarter method?
You should be able to simply inject the component into another class, for example a #RestController, and invoke the doDailyTask() method on it there.
Note: read the end of the answer for the way I implemented #Nonika's suggestions
What's the "right way" to send a websocket event on data insert?
I'm using a Spring Boot server with SQL/JPA and non-stomp websockets. I need to use "plain" websockets as I'm using Java clients where (AFAIK) there's no stomp support.
When I make a change to the database I need to send the event to the client so I ended up with an implementation like this:
#Transactional
public void addEntity(...) {
performActualEntityAdding();
sendEntityAddedEvent(eventData);
}
#Transactional
public void sendEntityAddedEvent(String eventData) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
sendEntityAddedEventAsync(eventData);
}
});
}
#Async
public void sendEntityAddedEventAsync(String eventData) {
// does the websocket session sending...
}
This works. If I would just call the sendEntityAddedEventAsync it would also work for real world scenarios but it fails on unit tests because the event would arrive before transaction commit. As such when the unit test invokes a list of the entities after the event it fails.
This feels like a hack that shouldn't be here. Is there a better way to ensure a commit?
I tried multiple alternative approaches and the problem is that they often worked for 10 runs of the unit tests yet failed every once in a while. That isn't acceptable.
I tried multiple approaches to solve this such as different transaction annotations and splitting the method to accommodate them. E.g read uncommitted, not supported (to force a commit) etc. Nothing worked for all cases and I couldn't find an authoritative answer for this (probably common) use case that wasn't about STOMP (which is pretty different).
Edit
One of my original attempts looked something like this:
// this shouldn't be in a transaction
public void addEntity(...) {
performActualEntityAdding();
sendEntityAddedEvent(eventData);
}
#Transactional
public void performActualEntityAdding(...) {
//....
}
#Async
public void sendEntityAddedEventAsync(String eventData) {
// does the websocket session sending...
}
The assumption here is that when sendEntityAddedEventAsync is invoked the data would already be in the database. It wasn't for a couple of additional milliseconds.
A few additional details:
Test environment is based on h2 (initially I mistakenly wrote hsql)
Project is generated by JHipster
Level 2 cache is used but disabled as NONE for these entities
Solution (based on #Nonika's answer):
The solution for me included something similar to this:
public class WebEvent extends ApplicationEvent {
private ServerEventDAO event;
public WebEvent(Object source, ServerEventDAO event) {
super(source);
this.event = event;
}
public ServerEventDAO getEvent() {
return event;
}
}
#Transactional
public void addEntity(...) {
performActualEntityAdding();
applicationEventPublisher.publishEvent(new WebEvent(this, evtDao));
}
#Async
#TransactionalEventListener
public void sendEntityAddedEventAsync(WebEvent eventData) {
// does the websocket session sending...
}
This effectively guarantees that the data is committed properly before sending the event and it runs asynchronously to boot. Very nice and simple.
Spring is using AdviceMode.PROXY for both #Async and #Transactional this is quote from the javadoc:
The default is AdviceMode.PROXY. Please note that proxy mode allows
for interception of calls through the proxy only. Local calls within
the same class cannot get intercepted that way; an Async annotation on
such a method within a local call will be ignored since Spring's
interceptor does not even kick in for such a runtime scenario. For a
more advanced mode of interception, consider switching this to
AdviceMode.ASPECTJ.
This rule is common for almost all spring annotations which requires proxy to operate.
Into your first example, you have a #Transactional annotation on both addEntity(..) and performActualEntityAdding(..). I suppose you call addEntity from another class so #Transactional works as expected. process in this scenario can be described in this flow
// -> N1 transaction starts
addEntity(){
performActualEntityAdding()//-> we are still in transaction N1
sendEntityAddedEvent() // -> call to this #Async is a class local call, so this advice is ignored. But if this was an async call this would not work either.
}
//N1 transaction commits;
That's why the test fails. it gets an event that there is a change into the db, but there is nothing because the transaction has not been committed yet.
Scenario 2.
When you don't have a #Transactional addEntity(..) then second transaction for performActualEntityAdding not starts as there is a local call too.
Options:
You can use some middleware class to call these methods to trigger
spring interceptors.
you can use Self injection with Spring
if you have Spring 5.0 there is handy #TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
I'm trying to run a few methods after the Spring Boot project starts. I'm using #EventListener(ApplicationReadyEvent.class) annotation above the methods I want ran after the project launches. But it's only starting for one method at a time. I want multiple methods started at once. Is that the expected behavior for #EventListener(ApplicationReadyEvent.class)?
Its OK to place several (more than one) methods annotated with #EventListener all of them will be executed:
#Configuration
public class SampleConfiguration {
#Bean
public SampleBean sampleBean() {return new SampleBean();}
#EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
System.out.println("Hello");
}
#EventListener
public void onApplicationReadyEvent2(ApplicationReadyEvent event) {
System.out.println("How are you");
}
}
This will print both "Hello" and "How are you" upon the succesfull start of the Application Context.
Now, its true that spring doesn't invoke them concurrently, it resolves all the listeners and calls them sequentially.
If you need a parallel execution you can create one listener that will be an "entry point" for the logical tasks that must be run in parallel and use Threads / Thread Pool Executors to run the code of your choice in parallel
Did you try adding #Async also above the method?
This listener is invoked synchronously. You can make it asynchronous by simply adding #Async annotation.
You can have the event listeners execute asynchronously by adding the following bean to your #Configuration class.
#Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster =
new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
If you've defined a custom TaskExecutor then you should replace new SimpleAsyncTaskExecutor() with yourCustomTaskExecutorBeanMethod()
I had run into a similar issue where ApplicationReadyEvent was annoted for 3 function but in debugging we found it to fire for only 1 always. We added #Order and kept that function with the highest index(i.e. lowest priority), as in our case it was running for an indefinite while loop, and found that all the 3 functions were invoked seamlessly.
In our project we are using ShedLock to prevents concurrent execution of scheduled Spring task. Sometimes we need to call this task manually, so to our front end we added a button which when clicked will call(API) this task. Now can we use ShedLock so that when the user clicks the button multiple times it will only execute the task one time and lock until it is complete.
You can certainly do that with dlock. Imagine Controller A and Service A where Controller A calls Service A. If you have a method doSomeWork at service A annotated by dlock, you can safely call the method from controller A as many as you want and it would run the service only once until the timeout. Here's a concrete example.
Service A should be something like this.
#TryLock(name = "doSomeWork", owner = "serviceA", lockFor = ONE_MINUTE)
public void doSomeWork() {
//...
}
Controller A would be something like this.
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public void doSomeWork() {
serviceA.doSomeWork();
}
See this post for more information.
From the API method, uou can use the lock manually as described in the documentation
LockingTaskExecutor executor // injected by Spring
...
Instant lockAtMostUntil = Instant.now().plusSeconds(600);
executor.executeWithLock(runnable, new LockConfiguration("lockName", lockAtMostUntil));
Did You tried to use proxy method?
#EnableSchedulerLock(mode = PROXY_METHOD, defaultLockAtMostFor = "PT30S")
https://github.com/lukas-krecan/ShedLock#scheduled-method-proxy