Need to understand asynchronous usage of Spring WebClient - spring-boot

I have a doubt regarding the usage of webclient in cirumstances when you need to invoke another service which is slow in responding and then use its data to process something and return in the call to your own api.
e.g. my doSometing method is called by service to retrive top order from a list.My service fetches the list from another service "order-service"
#RestController
public class TestController {
#Autowired
private TestService testService;
#GetMapping("/test")
public String doSomething() {
return WebClient.create()
.get()
.uri("http://localhost:9090/order-service")
.retrieve()
.bodyToMono(List.class)
.block().get(0).toString();
//may be do more processing on the list later.
}
As you can see , it currently blocks the calling thread which beats the purpose of async.Also , in future i might need to do more processing on this List before returning.
Am i using webclient correctly(is it serving any purpose here)?

Related

Cannot understand mocks

I'm trying to write tests for my Spring Boot application that has some end points, it's a REST application.
I have the "usual" simple web application with a controller, a service and a repository. CRUD operations.
In my update endpoint I call the service layer to perform the update, like this:
#PutMapping
public Post updatePost(#RequestBody Post post) {
return postService.updatePost(post);
}
The updatePost method on the PostService class makes some checks about the object before updating in it, and if the checks all pass, then the update operation is perforrmed, like this:
public Post updatePost(Post post) {
if (post == null || post.getId() == null) {
throw new PostGenericException();
}
Post postToUpdate = postRepo.findById(post.getId()).orElseThrow(PostGenericException::new);
bool isOk = true;
// some other checks..
if (!isOk) {
throw new PostGenericException();
}
// update operation
postToUpdate.setMessage(post.getMessage());
....
return postRepo.save(postToUpdate);
}
From what I've seen online in the test class I have to do something like this:
#WebMvcTest(PostController.class)
public class PostControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper mapper;
#MockBean
private PostService postService;
#Test
public void updatePost() throws Exception {
Post post = new Post(...);
Mockito.when(postService.updatePost(post)).thenReturn(post);
mockMvc.perform(MockMvcRequestBuilders.put("/posts")
.contentType(MediaType.APPLICATION_JSON)
.content(this.mapper.writeValueAsString(post)))
.andExpect(status().isOk())
.andExpect(jsonPath("$", notNullValue()));
}
}
So here in the test method I'm completely replacing the logic of the updatePost method of the service layer with a fake one.
How is this useful?
The only good reason I can think of is that here I'm trying to test the endpoint by itself, meaning that I simply want to check if I can reach that endpoint, but I don't really care about the implementation at all, i.e how the update operation is performed. I'm expecting that if I make a PUT request to that endpoint I get a result, if the test fails I know that the controller doesn't handler that endpoint anymore.
Is this all about it or am I missing something?
If I remember correctly, Kent Beck also said that you don't want to test the implementation but only the public APIs, it doesn't make much sense to test the implementation anyway, you could have a lot of tests and at some point have more test code than production code.
Using mocks may be against your testing philosophy, but looking at the practicalities:
Web layer is a nice chunk to be tested separately. There is a good amount of things that you can check:
routing
request and response deserialization
validation
error handling
authentication
This also allows business logic tests to skip these concerns.
Additional benefits:
they are easy to set up and run on a single machine (or even single process)
they are reasonably fast

Spring State Machine | Actions (Calling External API with data & pass data to another State)

I would like to use the Action<S,E> to call an external api. How can i add more data into this Action in order to invoke an external API? Another question is what if i want to send back the response (pass data to another State)
What is the best way to add more data? I'm trying to find an alternative of using context (which i know is possible but very ugly using Key-value).
Calling an external API is the same as any executing code, you can wire in your action any executable code. This includes autowiring a Service or Gateway and retrieve the data you need.
Regarding the second question, in my company we are using the extended state (context) to expose data. Before we release the state machine we get the data inside of it and serialise to a response object using object mapper.
Here is a snippet for illustration
#Configuration
#RequiredArgsConstructor
public class YourAction implements Action<States, Events> {
private final YourService service;
#Override
public void execute(final StateContext<States, Events> context) {
//getting input data examples
final Long yourIdFromHeaders = context.getMessageHeaders().get(key, Long.class);
final Long yourIdFromContext = context.getExtendedState().get(key, Long.class);
//calling service
final var responseData = service.getData(yourIdFromContext);
//storing results
context.getExtendedState().getVariables().put("response", responseData);
}

How to initialize Jackson on Spring Boot start to have fast 1st request?

Problem
I have a simple Spring Boot app with a basic RestController (full code available here). It consumes JSON and uses Jackson to convert request from JSON and response to JSON.
#RestController("/")
#RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public class SomeController {
#Autowired
private SomeService someService;
#PostMapping
public ResponseEntity<SomeResponseDto> post(#RequestBody #Valid SomeRequestDto someRequestDto) {
final SomeResponseDto responseDto = new SomeResponseDto();
responseDto.setMessage(someRequestDto.getInputMessage());
responseDto.setUuid(someService.getUuid());
return ResponseEntity.ok(responseDto);
}
After start-up, the 1st request is about 10-times slower than any sub-sequent request. I debugged and profiled the app and it seems that on first request a Jackson JSON parser is getting initialized somewhere in AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters and AbstractJackson2HttpMessageConverter.
In sub-sequent requests, it seems to get re-used.
Question
How do I initialize Jackson JSON parsing during start-up so that also 1st request is fast?
I know how to trigger a method after Spring started. In PreloadComponent I added as an example how to do a REST request against the controller.
#Component
public class PreloadComponent implements ApplicationListener<ApplicationReadyEvent> {
private final Logger logger = LoggerFactory.getLogger(PreloadComponent.class);
#Autowired
private Environment environment;
#Autowired
private WebClient.Builder webClientBuilder;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// uncomment following line to directly send a REST request on app start-up
// sendRestRequest();
}
private void sendRestRequest() {
final String serverPort = environment.getProperty("local.server.port");
final String baseUrl = "http://localhost:" + serverPort;
final String warmUpEndpoint = baseUrl + "/warmup";
logger.info("Sending REST request to force initialization of Jackson...");
final SomeResponseDto response = webClientBuilder.build().post()
.uri(warmUpEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.body(Mono.just(createSampleMessage()), SomeRequestDto.class)
.retrieve()
.bodyToMono(SomeResponseDto.class)
.timeout(Duration.ofSeconds(5))
.block();
logger.info("...done, response received: " + response.toString());
}
private SomeRequestDto createSampleMessage() {
final SomeRequestDto someRequestDto = new SomeRequestDto();
someRequestDto.setInputMessage("our input message");
return someRequestDto;
}
}
This only works in this toy example. In reality, I have many REST endpoints with complex DTOs and I would need to add a "warm-up" endpoint next to each "real" endpoint as I can't call my real endpoints.
What I already tried?
I added a second endpoint with a different DTO and called it in my PreloadComponent. This doesn't solve the problem. I assume that an Jackson / whatever instance is created for each type.
I autowired ObjectMapper into my PreloadComponent and parsed JSON to my DTO. Again, this doesn't solve the issue.
Full source available at: https://github.com/steinsag/warm-me-up
It turns out that Jackson validation is the problem. I added the JVM option
-verbose:class
to see when classes get loaded. I noticed that on 1st request, there are many Jackson validation classes getting loaded.
To confirm my assumption, I re-worked my example and added another independent warm-up controller with a distinct DTO.
This DTO uses all Java validation annotations also present like in the real DTO, e.g. #NotNull, #Min, etc. In addition, it also has a custom enum to also have validation of sub-types.
During start-up, I now do a REST request to this warm-up endpoint, which doesn't need to contain any business logic.
After start-up, my 1st request is now only 2-3 times slower than any sub-sequent requests. This is is acceptable. Before, the 1st request was 20-40 times slower.
I also evaluated if really a REST request is needed or if it is sufficient to just do JSON parsing or validation of a DTO (see PreloadComponent). This reduces runtime of 1st request a bit, but it is still 5-15 times slower than with proper warm-up. So I guess a REST request is needed to also load other classes in Spring Dispatcher, etc.
I updated my example at: https://github.com/steinsag/warm-me-up
I believe, that a lot of classes will be lazy-loaded. If first call performance is important, then I think warming up by calling each endpoint is the way to go.
Why do you say, that you cannot call the endpoints? If you have a database and you don't want to change the data, wrap everything in a transaction and roll it back after the warm up calls.
I haven't seen any other method to solve this, which doesn't necessarily mean, that it doesn't exist ;)

Spring Boot #Async method *sometimes* not invoked

My Spring Boot application provides the following REST controller which invokes two methods of a Service. One of these methods is annotated with #Async so it should run asynchronously.
The main application class is annotated with #EnableAsync.
The problem I observed is: Basically, the async method is executed. I can see the corresponding log entries in the production system. But it seems as if sometimes the async method does not get invoked. There are file ids in the database which do not appear in the logs.
Do you have any idea when this behavior could occur?
REST controller
#PostMapping(consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadDocument(#RequestParam("file") MultipartFile multipartFile) {
long fileId = fileService.save(multipartFile);
file.generateThumbnail(fileId);
return ResponseEntity.ok().build();
}
FileService
#Transactional
public long save(MultipartFile multipartFile) {
// saves the file...
return fileId;
}
#Async
#Transactional
public void generateThumbnail(long fileId) {
// generate thumbnail
log.info("Starting thumbnail generation for fileId {}", fileId);
file.setThumbnailId(thumbnailId);
}
Calling a async method from FileService does not work as async. You must create another Service for async method and call it from your service.
Reason:
self-invocation – calling the async method from within the same class
– won't work 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.
Resources: https://www.baeldung.com/spring-async

Obtaining the result of a Mono in order to pass it onto a JpaRepository or another non reactive class

I would like to know what is the appropriate way obtaining an object from a Mono (or Flux) to pass onto a non-reactive method such as a JpaRepository.
Here is the way I did it:
#Service
public class ReactiveAccountService {
//AccountService's methods take non-mono/non-flux objects as arguments
private AccountService accountService;
public ReactiveAccountService(AccountService accountService) {
this.accountService = accountService;
}
public Mono<Void> signUp(Mono<Account> accountMono) {
//Account is a Jpa entity
accountMono.subscribe(account -> accountService.signUp(account));
return Mono.empty();
}
}
How can this be improved? Can someone please advise?
The better way to do that is like this:
public Mono<Void> signUp(Mono<Account> accountMono) {
//Account is a Jpa entity
return accountMono.flatMap(account -> {
accountService.signUp(account);
return Mono.empty();
});
}
This way you follow Reactive Streams requirements and don't bother the execution flow with your blocking code. Plus according the Spring WebFlux requirements you don't subscribe in your own code, but deffer everything to the container which is an initiator of that Mono<Account>. The actual execution will happen, when there is enough resources in the Web (Reactive) container to subscribe to the returned Mono.

Resources