Is #Scheduled thread-safe? - java

I have a MongoRepository interface:
public interface MyRepository extends MongoRepository<MyDocument,String> {}
and a #Component annotated class which serves as a thread using #Scheduled :
#Component
#Scope("prototype")
#EnableAsync
public class MyThread {
#Autowired
private MyRepository myRepository;
#Scheduled(fixedRate = 5000)
#Async
public void saveCurrentTime()
{
myRepository.save(someTimeHere);
}
}
My question is:
Is this #Scheduled method thread-safe ? The main idea here is to create different MyThread beans (#Scope("prototype")) which will save the current time in a NoSQL. But what happens if 2 of these "threads" call at the same time myRepository.save() method ? Should I handle by myself the synchronization or is it already handled with this annotation ?

Related

How to run #Async method inside the #Scheduled one

I have read many questions and answers about using #Scheduled with #Async in Spring but no one resolves my problem and my asynchronous method still runs single-threaded. So here is my Configuration class:
#EnableScheduling
#EnableAsync
#Configuration
#RequiredArgsConstructor
public class SchedulerConfiguration {
private final ThreadPoolProperties threadPoolProperties;
#Bean
public TaskExecutor commonTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); // 10
taskExecutor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); // 20
taskExecutor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); // 5
taskExecutor.setThreadNamePrefix("TEST");
taskExecutor.initialize();
return taskExecutor;
}
}
Then we have a bean with the #Scheduled method:
#Component
#RequiredArgsConstructor
public class ScheduledTask {
private final ConfirmReservationTask confirmReservationTask;
#Scheduled(cron = "${booking.scheduler.confirmReservationsCron}")
public void process() {
confirmReservationTask.confirmReservations();
}
}
And finally, another bean (to avoid self-injection and proxy problems with asynchronous processing) with #Async method:
#Log4j2
#Component
#RequiredArgsConstructor
public class ConfirmReservationTask {
private final ReservationService reservationService;
#Async("commonTaskExecutor")
public void confirmReservations() {
...
}
}
unfortunately, this solution works in only one thread, however, the method uses the correct ThreadExecutor. How to solve it?

How to make Aysnc annotation work in Spring Boot?

I created a simple spring boot application with scheduled (#Scheduled) task. In that scheduled task, I would like to call async function with #Async, but I can see it still runs on the scheduling thread without switch to another thread. I also tried to customise executor, but no luck. Here are some codes.
I also already enable async in main class
public class scheduledService {
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
AsyncService service = new AsyncService();
service.asyncCall();
service.asyncCall();
service.asyncCall();
asyncCall();
}
}
public class AsyncService {
#Async()
public void asyncCall(){
System.out.printly("Current thread -- {}",Thread.currentThread().getName()))
Thread.sleep(10000);
}
}
#Bean(name = "MyThreadPoolExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyThreadPoolExecutor-");
executor.initialize();
return executor;
}
#SpringBootApplication
#EnableScheduling
#EnableAsync
public class ScheduledApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(ScheduledApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
according to Baeldung:
#Async has two limitations:
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won't work
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
so you can put your async method in a service and use it from there
you need to autowire AsyncService, do not create new object like
AsyncService service = new AsyncService();
Also, annotate your scheduledService class with #Service or #Component
#Service
public class scheduledService {
#Autowired
private AsyncService service ;
#Scheduled(fixedRateString = "${config.scheduleInterval}")
public void pollDataWithFixSchedule() {
service.asyncCall();
service.asyncCall();
service.asyncCall();
}
}
不要在同一个类中调用异步方法
Do not call asynchronous methods in the same class.
将异步任务单独放到一个类 并且在这个类上加上#Component
Put asynchronous tasks into a single class and add # component to this class
Use #EnableAsync on the top of class where you are creating async bean, not on the ScheduledApplication.

JUnit-testing a Spring #Async void service method

I have a Spring service:
#Service
#Transactional
public class SomeService {
#Async
public void asyncMethod(Foo foo) {
// processing takes significant time
}
}
And I have an integration test for this SomeService:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
Here is the problem:
as SomeService.asyncMethod(..) is annotated with #Async and
as the SpringJUnit4ClassRunner adheres to the #Async semantics
the testAsyncMethod thread will fork the call someService.asyncMethod(testData) into its own worker thread, then directly continue executing verifyResults(), possibly before the previous worker thread has finished its work.
How can I wait for someService.asyncMethod(testData)'s completion before verifying the results? Notice that the solutions to How do I write a unit test to verify async behavior using Spring 4 and annotations? don't apply here, as someService.asyncMethod(testData) returns void, not a Future<?>.
For #Async semantics to be adhered, some active #Configuration class will have the #EnableAsync annotation, e.g.
#Configuration
#EnableAsync
#EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
//
}
To resolve my issue, I introduced a new Spring profile non-async.
If the non-async profile is not active, the AsyncConfiguration is used:
#Configuration
#EnableAsync
#EnableScheduling
#Profile("!non-async")
public class AsyncConfiguration implements AsyncConfigurer {
// this configuration will be active as long as profile "non-async" is not (!) active
}
If the non-async profile is active, the NonAsyncConfiguration is used:
#Configuration
// notice the missing #EnableAsync annotation
#EnableScheduling
#Profile("non-async")
public class NonAsyncConfiguration {
// this configuration will be active as long as profile "non-async" is active
}
Now in the problematic JUnit test class, I explicitly activate the "non-async" profile in order to mutually exclude the async behavior:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
#ActiveProfiles(profiles = "non-async")
public class SomeServiceIntTest {
#Inject
private SomeService someService;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
verifyResults();
}
// verifyResult() with assertions, etc.
}
I have done by injecting
ThreadPoolTaskExecutor
and then
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
before verifying results,
it as below:
#Autowired
private ThreadPoolTaskExecutor executor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
someService.asyncMethod(testData);
executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
verifyResults();
}
If you are using Mockito (directly or via Spring testing support #MockBean), it has a verification mode with a timeout exactly for this case:
https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#22
someAsyncCall();
verify(mock, timeout(100)).someMethod();
Much more capable is the great library Awaitility, which has many options how to handle async assertions. Example:
someAsyncCall();
await().atMost(5, SECONDS)
.untilAsserted(() -> assertThat(userRepo.size()).isEqualTo(1));
In case your method returns CompletableFuture use join method - documentation CompletableFuture::join.
This method waits for the async method to finish and returns the result. Any encountered exception is rethrown in the main thread.
Just to extend the answer by #bastiat, which in my opinion should be considered the correct one, you should also specified the TaskExecutor, if you are working with multiple executors. So you would need to inject the correct one that you wish to wait for. So, let's imagine we have the following configuration class.
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean("myTaskExecutor")
public TaskExecutor myTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(15);
executor.setCoreCapacity(10);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setThreadNamePrefix("MyTaskExecutor-");
executor.initialize();
return executor;
}
// Everything else
}
Then, you would have a service that would look like the following one.
#Service
public class SomeServiceImplementation {
#Async("myTaskExecutor")
public void asyncMethod() {
// Do something
}
// Everything else
}
Now, extending on #bastiat answer, the test would look like the following one.
#Autowired
private SomeService someService;
#Autowired
private ThreadPoolTaskExecutor myTaskExecutor;
#Test
public void testAsyncMethod() {
Foo testData = prepareTestData();
this.someService.asyncMethod(testData);
this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
this.verifyResults();
// Everything else
}
Also, I have a minor recommendation that has nothing to do with the question. I wouldn't add the #Transactional annotation to a service, only to the DAO/repository. Unless you need to add it to a specific service method that must be atomic.
Just addition to the above solutions:
#Autowired
private ThreadPoolTaskExecutor pool;
#Test
public void testAsyncMethod() {
// call async method
someService.asyncMethod(testData);
boolean awaitTermination = pool.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
assertThat(awaitTermination).isFalse();
// verify results
}

Injecting the TaskScheduler with Spring

Is it possible to inject the TaskScheduler instance created by Spring?
I would like to schedule tasks programatically and for that, I guess I need to access the TaskScheduler but for some reason, it's not found by Spring for autowiring.
#Configuration
#EnableScheduling
public class MySpringConfig {
}
#Component
public class MyClass implements InitializingBean {
#Autowired
private TaskScheduler taskScheduler;
#Override
public void afterPropertiesSet() throws Exception {
...
}
}
Any idea?
Thanks!
#Configuration
#EnableScheduling
public class MySpringConfig {
#Bean
public TaskScheduler taskScheduler() {
//org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
return new ThreadPoolTaskScheduler();
}
}
You can choose which ever implementation you like. ThreadPoolTaskScheduler is the simpler one as mentioned in this link.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations

How to share instances between Prototypes (Spring LoC)

To run a Job in a TaskExecutor I need to instantiate new Jobs implementing the Runnable Interface. To solve this, i will create a new Spring Prototype Bean named Job "on Demand".
But in my application a Job has two fields LocationChanger and QueryTyper. These two should share the same WebDriver instance created by a WebDriverFactory.
Now the question is how to design this with Spring?
This is the related code:
#Component
#Scope("prototype")
public class Job implements Runnable {
#Autowired
LocationChanger locationChanger;
#Autowired
QueryTyper queryTyper;
#Override
public void run() {
// at this point the locationChanger and
// queryTyper should share the same instance
}
}
#Component
#Scope("prototype")
public class LocationChanger {
#Autowired
#Qualifier(...) // For every new Job Created, the same WebDriver instance should be injected.
WebDriver webDriver
}
#Component
#Scope("prototype")
public class QueryTyper {
#Autowired
#Qualifier(...) // For every new Job Created, the same WebDriver instance should be injected.
WebDriver webDriver
}
public class WebDriverFactoryBean implements FactoryBean<WebDriver> {
#Override
public WebDriver getObject() throws Exception {
return // createdAndPrepare...
}
#Override
public boolean isSingleton() {
return false;
}
}
Thanks a lot!
Update 1:
A possible solution could be to autowire the WebDriver in the Job only and then in a #PostConstruct inject this WebDriver to the LocationChanger and QueryTyper. But then i wire by hand.
#Component
#Scope("prototype")
public class Job implements Runnable {
#Autowired
LocationChanger locationChanger;
#Autowired
QueryTyper queryTyper;
#Autowired
WebDriver webDriver;
#PostConstruct
public void autowireByHand() {
locationChanger.setWebDriver(this.webDriver);
queryTyper.setWebDriver(this.webDriver);
}
}
// + remove all #Autowired WebDriver's from LocationChanger and QueryTyper
If I understand your requirement, you need WebDriver to be shared between a Job and LocationChanger. So it's not prototype scope, and it's not singleton scope. To solve this, I think you either have to do it by hand, as you suggest, or you could try to implement your own scope, as described in the Spring reference documentation
Edit
I don't think you "handwired" solution looks that bad BTW.

Categories

Resources