Spring-Integration AbstractRequestHandlerAdvice with webflux/reactive: does this introduce a synchronous process? - spring-integration

I've implemented an advice handler attached to outbound-gateway components in order to be able to long outgoing HTTP requests before/after. Originally, this was for a Spring-Integration project using MVC/servlet underpinnings. (Original question: Logging http request in Spring integration http outbound gateway )
We are attempting a migration to webflux/reactive which means that we are using a webflux:outbound-gateway in lieu of a http:outbound-gateway.
The advice handler is an around call in the doInvoke() method:
public class MyLogger extends AbstractRequestHandlerAdvice {
// ...
#Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message(
{
// ... logging before the call ...
Object result = callback.execute();
// ... logging after the call ...
return result;
}
}
Whereas I understand that the webflux:outbound-gateway call itself is supposed to be asynchronous (i.e. no threads waiting on a response), it would seem to me that the advice handler itself must be implemented synchronously (i.e. thread awaiting the response). Am I understanding correctly? Is the advice handler adding synchronicity that wouldn't otherwise be there?
Thanks for any insights on this point.

There is a ReactiveRequestHandlerAdvice for this particular use-case to wrap a reply Mono with anything you'd like: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#reactive-advice

Related

Spring Boot Rest-Controller restrict multithreading

I want my Rest Controller POST Endpoint to only allow one thread to execute the method and every other thread shall get 429 until the first thread is finished.
#ResponseStatus(code = HttpStatus.CREATED)
#PostMapping(value ="/myApp",consumes="application/json",produces="application/json")
public Execution execute(#RequestBody ParameterDTO StartDateParameter)
{
if(StartDateParameter.getStartDate()==null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}else {
if(Executer.isProcessAlive()) {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS);
}else {
return Executer.execute(StartDateParameter);
}
}
}
When I send multithreaded requests, every request gets 201. So I think the requests get in earlier than the isAlive() method beeing checked. How can I change it to only process the first request and "block" every other?
Lifecycle of a controller in spring is managed by the container and by default, it is singleton, which means that there is one instance of the bean created at startup and multiple threads can use it. The only way you can make it single threaded is if you use a synchronized block or handle the request call through an Executor service. But that defeats the entire purpose of using spring framework.
Spring provides other means to make your code thread safe. You can use the #Scope annotation to override the default scope. Since you are using a RestController, you could use the "request" scope (#Scope("request")), which creates a new instance to process your every http request. Doing it this way will make ensure that only 1 thread will be accessing your controller code at any given time.

Handling Error in asynchronous process using #Async annotation on custom method vs on #Gateways method

I have a process where I need to upload file to Sftp server asynchronously. So after exploring more about Async in gateways I found that I need to have error channel defined in #MessagingGateway parameter then handler to handle the Exception propagated to error channel but I felt handling this way is complicated for me, as I will have to update Pojo field and persist into DB depending on the file upload, success or failure.
So I thought of having a custom method annotated with #Async and call the gateway method. Also surround gateway method with try block and catch any exception occurred in the downstream
Code Sample:
#Async
void upload(Resource file, FileStatus fileStatus){
try{
uploadGateway.upload(file,fileStatus.getFilePath(),fileStatus.getFileName());
}catch(RuntimeException e){
fileStatus.setUploadStatus("Failed");
//save into db
}
}
Upload Gateway without error channel so that error can be sent back to caller
#MessagingGateway
public interface UploadGateway {
#Gateway(requestChannel = "input.channel")
void upload(#Payload Resource file, #Header("path") String path, #Header("name") String fileName);
}
Handler:
#Bean
public IntegrationFlow uploadDocument() {
return IntegrationFlows.from("input.channel")
.log(LoggingHandler.Level.WARN)
.handle(Sftp.outboundAdapter(sftpSessionFactory(), FileExistsMode.FAIL)
.autoCreateDirectory(true)
.remoteDirectoryExpression("headers['path']")
.fileNameExpression("headers['name']"))
.get();
}
Question:
What will be the consequences if I'm handling error this way? Is this the right way to handle any error occurred in downstream flow?
Since #MessagingGateway is like an RPC in messaging, it is fully OK to catch an exception on its method call like that. Since you make your flow fully sync, it works like typical Java exceptions sub-system.
Your concern about async error handling with errorChannel really makes sense since it is similar in complexity with standard Java async method handling and its errors processing.
On the other hand it is really commended to handle errors downstream via errorChannel if that is going to be some complex logic in some other flow. Plus you are going to return back some compensation message.
However in the end of day the choice is yours: there is no drawbacks to handle errors yourself.
See Error Handling chapter for more food to think on.

Quarkus Transactions on different thread

I have a quarkus application with an async endpoint that creates an entity with default properties, starts a new thread within the request method and executes a long running job and then returns the entity as a response for the client to track.
#POST
#Transactional
public Response startJob(#NonNull JsonObject request) {
// create my entity
JobsRecord job = new JobsRecord();
// set default properties
job.setName(request.getString("name"));
// make persistent
jobsRepository.persist(job);
// start the long running job on a different thread
Executor.execute(() -> longRunning(job));
return Response.accepted().entity(job).build();
}
Additionally, the long running job will make updates to the entity as it runs and so it must also be transactional. However, the database entity just doesn't get updated.
These are the issues I am facing:
I get the following warnings:
ARJUNA012094: Commit of action id 0:ffffc0a80065:f2db:5ef4e1c7:0 invoked while multiple threads active within it.
ARJUNA012107: CheckedAction::check - atomic action 0:ffffc0a80065:f2db:5ef4e1c7:0 commiting with 2 threads active!
Seems like something that should be avoided.
I tried using #Transaction(value = TxType.REQUIRES_NEW) to no avail.
I tried using the API Approach instead of the #Transactional approach on longRunning as mentioned in the guide as follows:
#Inject UserTransaction transaction;
.
.
.
try {
transaction.begin();
jobsRecord.setStatus("Complete");
jobsRecord.setCompletedOn(new Timestamp(System.currentTimeMillis()));
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
}
but then I get the errors: ARJUNA016051: thread is already associated with a transaction! and ARJUNA016079: Transaction rollback status is:ActionStatus.COMMITTED
I tried both the declarative and API based methods again this time with context propagation enabled. But still no luck.
Finally, based on the third approach, I thought keeping the #Transactional on the Http request handler and leaving longRunning as is without declarative or API based transaction approaches would work. However the database still does not get updated.
Clearly I am misunderstanding how JTA and context propagation works (among other things).
Is there a way (or even a design pattern) that allows me to update database entities asynchronously in a quarkus web application? Also why wouldn't any of the approaches I took have any effect?
Using quarkus 1.4.1.Final with ext: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, kubernetes-client, mutiny, narayana-jta, rest-client, resteasy, resteasy-jackson, resteasy-mutiny, smallrye-context-propagation, smallrye-health, smallrye-openapi, swagger-ui]
You should return an async type from your JAX-RS resource method, the transaction context will then be available when the async stage executes. There is some relevant documentation in the quarkus guide on context propagation.
I would start by looking at the one of the reactive examples such as the getting started quickstart. Try annotating each resource endpoint with #Transactional and the async code will run with a transaction context.

Error Handling in single threaded applications

In a single threaded Spring Integration, application where the entire pipleine is running within the same transaction, an error channel is not applicable. Instead, exceptions will be thrown back to the caller.
The way I am doing this is to define a service activator which is the first of a chain of components which handle an incoming message.
#ServiceActivator
public Message handleException(Message message) {
try {
return message;
}
catch (Throwable throwable){
// HANDLE ERROR
}
return null;
}
Is this the correct approach, or is there a better strategy?
Thanks
No; that won't do anything at all; it will simply pass the message to the next element in the chain. If you don't need to handle the error in any way, and just want it thrown back to the caller, you need to do nothing. If the start of the flow is a gateway, the gateway will unwrap the cause from the MessagingException.
You can, of course, put an error-channel on the gateway and handle it there.
It's generally best to provide some more details about your flow (entry point etc), when asking these general questions.

Use Task.Factory.StartNew in MVC 4 async ApiController?

I'm using MVC4 ApiController to upload data to Azure Blob. Here is the sample code:
public Task PostAsync(int id)
{
return Task.Factory.StartNew(() =>
{
// CloudBlob.UploadFromStream(stream);
});
}
Does this code even make sense? I think ASP.NET is already processing the request in a worker thread, so running UploadFromStream in another thread doesn't seem to make sense since it now uses two threads to run this method (I assume the original worker thread is waiting for this UploadFromStream to finish?)
So my understanding is that async ApiController only makes sense if we are using some built-in async methods such as HttpClient.GetAsync or SqlCommand.ExecuteReaderAsync. Those methods probably use I/O Completion Ports internally so it can free up the thread while doing the actual work. So I should change the code to this?
public Task PostAsync(int id)
{
// only to show it's using the proper async version of the method.
return TaskFactory.FromAsync(BeginUploadFromStream, EndUploadFromStream...)
}
On the other hand, if all the work in the Post method is CPU/memory intensive, then the async version PostAsync will not help throughput of requests. It might be better to just use the regular "public void Post(int id)" method, right?
I know it's a lot questions. Hopefully it will clarify my understanding of async usage in the ASP.NET MVC. Thanks.
Yes, most of what you say is correct. Even down to the details with completion ports and such.
Here is a tiny error:
I assume the original worker thread is waiting for this UploadFromStream to finish?
Only your task thread is running. You're using the async pipeline after all. It does not wait for the task to finish, it just hooks up a continuation. (Just like with HttpClient.GetAsync).

Resources