Long computation AJAX causing duplicate controller Play Framework controller action calls - ajax

Basic Problem:
If I make an AJAX call to a controller method that performs a long computation (60 seconds or greater), I get a duplicate thread that comes in and follows the same path of execution (as best as I can tell from stack trace dumps -- and only one, this doesn't continue happening with the second thread). This appears to only happen when the controller action is called via AJAX. This can easily be replicated by creating a dummy controller method with nothing in it by a Thread.sleep() call that returns when finished.
I've tested this in a method that's loaded without an AJAX call and it doesn't produce the rogue thread. I tried various forms of AJAX calls (several forms of jQuery methods and base JavaScript) and got the same result with each. I initially thought it might be a thread problem so I implemented the dummy method using Promise(s) (http://www.playframework.com/documentation/2.1.x/JavaAsync, and http://www.playframework.com/documentation/2.1.x/JavaAkka) and AsyncResult, but it had no effect.
I know that the two threads are using the same execution context. Is that causing the problem here? Is it avoidable by moving the long computation to another context? Any ideas as to where this second, duplicate thread is coming from?
Controller Method (Long Computation):
public static Result test()
{
Logger.debug("*** TEST Controller entry: threadId=" + Thread.currentThread().getId());
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
for(StackTraceElement e : stack)
{
Logger.debug("***" + e.toString());
}
Promise<Void> promiseString = Akka.future(
new Callable<Void>() {
public Void call() {
try
{
Logger.debug("*** going to sleep: threadId=" + Thread.currentThread().getId());
Thread.sleep(90000);
}
catch(InterruptedException e)
{
//swallow it whole and move on
}
return null;
}
}
);
Promise<Result> promiseResult = promiseString.map(
new Function<Void, Result>() {
public Result apply(Void voidParam) {
return ok("done");
}
}
);
return async(promiseResult);
}

Related

NetworkStream ReadAsync and WriteAsync hang infinitelly when using CancellationTokenSource - Deadlock Caused by Task.Result (or Task.Wait)

After reading pretty much every question on Stack Overflow and Microsoft's documentation about NetworkStream, I dont understand what is wrong with my code.
The problem I see is that my method GetDataAsync() hangs very often. I call this method from Init Method like so:
public MyView(string id)
{
InitializeComponent();
MyViewModel myViewModel = session.Resolve<MyViewModel>(); //Autofac
myiewModel.Init(id);
BindingContext = myViewModel;
}
Above, my View does its initialization, then resolves MyViewModel from Autofac DiC and then calls MyViewModel Init() method to do some additional setup on the VM.
The Init method then calls my Async method GetDataAsync which return a IList like so:
public void Init()
{
// call this Async method to populate a ListView
foreach (var model in GetDataAsync("111").Result)
{
// The List<MyModel> returned by the GetDataAsync is then
// used to load ListView's ObservableCollection<MyModel>
// This ObservableCollection is data-bound to a ListView in
// this View. So, the ListView shows its data once the View
// displays.
}
}
, and here is my GetDataAsync() method including my comments:
public override async Task<IList<MyModel>> GetDataAsync(string id)
{
var timeout = TimeSpan.FromSeconds(20);
try
{
byte[] messageBytes = GetMessageBytes(Id);
using (var cts = new CancellationTokenSource(timeout))
using (TcpClient client = new TcpClient(Ip, Port))
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(messageBytes, 0, messageBytes.Length, cts.Token);
await stream.FlushAsync(cts.Token);
byte[] buffer = new byte[1024];
StringBuilder builder = new StringBuilder();
int bytesRead = 0;
await Task.Delay(500);
while (stream.DataAvailable) // need to Delay to wait for data to be available
{
bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);
builder.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, bytesRead));
}
string msg = buffer.ToString();
}
return ParseMessageIntoList(msg); // parses message into IList<MyModel>
}
catch (OperationCanceledException oce)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
catch (Exception ex)
{
return await Task.FromResult<IList<RoomGuestModel>>(new List<RoomGuestModel>());
}
}
I would expect that a ReadAsync or WriteAsync either complete successfully, throw some exception, or get cancelled after 10 seconds in which case I would catch OperationCanceledException.
However, it just hangs endlessly when I call method above. If I am debugging and have some breakpoints in the code above, I will be able to go through the method entirely but if I call it 2nd time, app just hangs forever.
I am new to Tasks and Async programming, so I am also not sure I do my cancellations and exception handling properly here?
UPDATE AND FIX
I figured out how to fix the deadlock issue. In hope this will help others sho might run into the same issue, I'll first explain it. The articles that helped me a lot are:
https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-my/ by Stephen Taub
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/ by James Montemagno
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx by StephenCleary
https://blog.xamarin.com/getting-started-with-async-await/ by Jon Goldberger
#StephenCleary was great help understanding the issue. Calling Result or Wait (above, I call Result when calling GetDataAsync) will lead to dead-lock.
The context thread (UI in this case) is now waiting for GetDataAsync to complete, but GetDataAsync captures the current context-thread (UI thread), so it can resume on it once it gets data from TCP. But since this context-thread is now blocked by call to Result, it cannot resume.
The end result is that it looks like call to GetDataAsync has deadlocked but in reality, it is call to Result that deadlocked.
After reading tons of articles from #StephenTaub, #StephenCleary, #JamesMontemagno, #JoeGoldenberger (thank you all), I started getting understanding of the issue (I am new to TAP/async/await).
Then I discovered continuations in Tasks and how to use them to resolve the issue (thanks to Stephen Taub's article above).
So, instead of calling it like:
IList<MyModel> models = GetDataAsync("111").Result;
foeach(var model in models)
{
MyModelsObservableCollection.Add(model);
}
, I call it with continuation like this:
GetDataAsync(id)
.ContinueWith((antecedant) =>
{
foreach(var model in antecedant.Result)
{
MyModelsObservableCollection.Add(model);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith((antecedant) =>
{
var error = antecedant.Exception.Flatten();
}, TaskContinuationOptions.OnlyOnFaulted);
This seam to have fixed my deadlocking issue and now my list will load fine even though it is loaded from the constructor.
So, this seam to work just fine. But #JoeGoldenberger also suggests another solution in his article https://blog.xamarin.com/getting-started-with-async-await/ which is to use Task.Run(async()=>{...}); and inside that await GetDataAsync and load ObservableCollection. So, I gave that a try as well and that is not blocking either, so working great:
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
So, it looks like either of these 2 will remove deadlock just fine. And since above my Init method is called from a c-tor; therefore, I cannot make it Async and await on this, using one of the 2 methods described above resolves my problem. I dont know which one is better but in my tests, they do work.
Your problem is most likely due to GetDataAsync("111").Result. You shouldn't block on async code.
This can cause deadocks. E.g., if you're on a UI thread, the UI thread will start GetDataAsync and run it until it hits an await. At this point, GetDataAsync returns an incomplete task, and the .Result call blocks the UI thread until that task is completed.
Eventually, the inner async call completes and GetDataAsync is ready to resume executing after its await. By default, await captures its context and resumes on that context. Which in this example is the UI thread. Which is blocked since it called Result. So, the UI thread is waiting for GetDataAsync to complete, and GetDataAsync is waiting for the UI thread so it can complete: deadlock.
The proper solution is to go async all the way; replace .Result with await, and make the necessary changes to other code for that to happen.
As stated in my update, going async all the way by providing an async lambda like below resolved the issue for me
Task.Run(async() =>
{
IList<MyModel> models = await GetDataAsync(id);
foreach (var model in models)
{
MyModelsObservableCollection.Add(model);
}
});
Loading asynchronously an observable collection in a ctor this way (in my case, ctor calls Init which then uses this Task.Run) solves problem

exit() method entered without exit command

I have Processing 3 code that is exhibiting some odd behavior. I have a void exit() method defined that is being executed at random times without the user actually telling the code to exit. Here is the method:
void exit()
{
println("clearing buffer and closing file");
if (output != null) {
print("output is not null");
try {
output.close();
}
catch (IOException e) {
println("Error while closing the writer");
}
}
super.exit();
}
As you can see, the only thing that it does is attempt to close a buffered writer called output. Flushing this writer is not critical, so for now I am just removing it from my sketch. But long term, I am curious how this can be happening. Nowhere else in my code is the exit method explicitly called. IE, the code cannot decide to exit. It is only when the user closes the problem using the X.
Note: I cannot upload the entire code this method is attached too because it is too long. I think perhaps a better way to phrase my questions would be something like:
"Hi, I am a noob that doesn't know anything about the exit method. Is there anyway that this method could somehow get called without me explicitly calling it or hitting the exit button?"
Add this at the beginning of your exit() method.
new Exception().printStackTrace();
The resulting stacktrace should allow you to figure out what is calling your exit() method.
Or if you can't tweak the code, you can run the application using a debugger and set a breakpoint at the start of the exit() method.
To answer your question about whether it is possible, the answer depends on what you mean by "without me explicitly calling". There are various ways to call a method, some of which are quite obscure; e.g.
You can use reflection to get the Method object for the exit method from the declaring class, and then call invoke(...) on it.
You can call a Java method from native code via the JNI or JNA apis.
You can generate Java source code that contains an exit() call, compile it, load it, and run it.
You can insert an exit() call into an "innocent" method using BCEL or similar.
And ...
If there is a debug agent attached to the JVM, the debugger can call exit() on some thread in the JVM.
In short, the answer to your question is Yes.
Your method could be found and invoked dynamically using reflection by any class in the same classloader or any other that is below in the hierarchy.
Also, it has a default access. So it could be invoked statically by any class in the same package.
+1 for #Andres, reflection is one possibility.
Have you tried using a breakpoint on the method and looking at the thread's stacktrace?
Personally I don't use breakpoints (just my style) and would try and look at the thread programmatically. Maybe some of the following code can help you look at the thread and get an idea of what's going on:
public class ThreadUtil {
/** Blocked constructor **/
private ThreadUtil() {
}
/**
* Get the stackstrace of the current {#link Thread}.
* The stacktrace will be returned in the form of a string.
*/
public static String getStackTrace() {
return getStackTrace(Thread.currentThread());
}
/**
* Get the stackstrace of a {#link Thread}.
* The stacktrace will be returned in the form of a string.
*/
public static String getStackTrace(Thread thread) {
StringBuilder sb = new StringBuilder();
StackTraceElement[] currThreadStackTraceElementList = thread.getStackTrace();
appendStackTrace(sb, currThreadStackTraceElementList);
return sb.toString();
}
public static String getAllStackTraces() {
StringBuilder sb = new StringBuilder();
Map<Thread, StackTraceElement[]> threadList = Thread.getAllStackTraces();
for (StackTraceElement[] currThreadStackTraceElementList : threadList.values()) {
appendStackTrace(sb, currThreadStackTraceElementList);
}
return sb.toString();
}
private static void appendStackTrace(StringBuilder sb,
StackTraceElement[] currThreadStackTraceElementList) {
sb.append("Thread stack trace: \n");
for (StackTraceElement currThreadStackTraceElement : currThreadStackTraceElementList) {
sb.append("\t" + currThreadStackTraceElement + "\n");
}
sb.append("\n");
}
}
It's a Processing-specific thing.
void exit() is a method already defined by processing in PApplet.java
As explained in the reference:
Rather than terminating immediately, exit() will cause the sketch to
exit after draw() has completed (or after setup() completes if called
during the setup() function).
For Java programmers, this is not the same as System.exit(). Further,
System.exit() should not be used because closing out an application
while draw() is running may cause a crash (particularly with P3D).
exit() it is expected to be used something like this:
void draw() {
line(mouseX, mouseY, 50, 50);
}
void mousePressed() {
exit();
}
It is called within PApplet.java in a few places, notably in handleKeyEvent to close the sketch when ESC is pressed, or when ⌘w is pressed.
Just rename your method to something other than exit()

Data Fetching Crashes in Xamarin Forms

I am trying to fetch Customer data to parse them into customer object to display on TableView. The following code sometimes works, sometimes not. Whenever it does crash, it shows Customer data is empty in the foreach loop even though I run the same code every time. I do not have clue what could be wrong in this circumstances. I am quite new on this platform. If I am missing anything/ extra information, please let me know.
namespace TableViewExample
{
public partial class MyDataServices : ContentPage
{
private ODataClient mODataClient;
private IEnumerable <IDictionary<string,object>> Customers;
public MyDataServices ()
{
InitializeComponent ();
InitializeDataService ();
GetDataFromOdataService ();
TableView tableView = new TableView{ };
var section = new TableSection ("Customer");
foreach (var customers in Customers) {
//System.Diagnostics.Debug.WriteLine ((string)customers ["ContactName"]);
var name = (string)customers ["ContactName"];
var cell = new TextCell{ Text = name };
section.Add (cell);
}
tableView.Root.Add (section);
Padding = new Thickness (10, 20, 10, 10);
Content = new StackLayout () {
Children = { tableView }
};
}
private void InitializeDataService(){
try {
mODataClient = new ODataClient ("myURL is here");
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
private void GetDataFromOdataService (){
try {
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
}
}
Its hard helping out here, however here are some things to consider:-
It sounds like the dataservice could either be not contactable / offline; too busy or it could even be throwing an exception itself and returning a data response that you are not expecting to receive, that then triggers an exception and crash in your application as your always expecting an exact response without catering for any abnormal responses / events.
If you are contacting an external service over the internet it may just be your internet connection is slow / faulty and not returning the information fast enough as other possibilities.
In your code you are assuming that you always get a response from the server - and that this response will always be of an anticipated structure that your expecting to decode - without factoring in any possibility of abnormal responses returned by the dataservice. I have not used ODataClient personally, so not sure how it behaves in the event of maybe no data received / timeout or in your case the dataservice and how it behaves internally in the response to a bad-request etc.
I am assuming an exception would get thrown, and you do get your debug line executed indicating a failure.
You may want to also adjust this statement so that you write out the exception as well, i.e.:-
private void GetDataFromOdataService ()
{
try
{
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR!" + ex.ToString());
}
}
If there was a bad response, then the line at Customers = ..... would throw the exception as there may be no Customers returned or some other information packaged in the response from the dataservice.
The Customers variable would also be null at this point I am assuming due to this failing.
So when you get back to your code at foreach (var customers in Customers) { it will then throw a null reference exception as Customers is infact null.
As all your current code executes in the constructor without any try and catch block around this, it will also crash your application at this point as well.
Also you are doing all of this work in the constructor. Try seperating this out. I haven't investigated exactly where the constructor gets called in an iOS page life-cycle, however, if it is in the viewDidLoad, then you have something like 10 seconds for everything to complete, otherwise it will exit automatically. I imagine in your case, this isn't applicable however.
Going forward also try putting your layout controls in the constructor, and move your data task to maybe the OnAppearing override instead.
Using async would definitely be advisable as well, but remember you need to inspect the response from your dataservice, as the error could be embedded within the response also and you will need to detect when it is OK to process the data.

How can a JSF/ICEfaces component's parameters be updated immediately?

I have an ICEfaces web app which contains a component with a property linked to a backing bean variable. In theory, variable value is programmatically modified, and the component sees the change and updates its appearance/properties accordingly.
However, it seems that the change in variable isn't "noticed" by the component until the end of the JSF cycle (which, from my basic understanding, is the render response phase).
The problem is, I have a long file-copy operation to perform, and I would like the the inputText component to show a periodic status update. However, since the component is only updated at the render response phase, it doesn't show any output until the Java methods have finished executing, and it shows it all changes accumulated at once.
I have tried using FacesContext.getCurrentInstance().renderResponse() and other functions, such as PushRenderer.render(String ID) to force XmlHttpRequest to initialize early, but no matter what, the appearance of the component does not change until the Java code finishes executing.
One possible solution that comes to mind is to have an invisible button somewhere that is automatically "pressed" by the bean when step 1 of the long operation completes, and by clicking it, it calls step 2, and so on and so forth. It seems like it would work, but I don't want to spend time hacking together such an inelegant solution when I would hope that there is a more elegant solution built into JSF/ICEfaces.
Am I missing something, or is resorting to ugly hacks the only way to achieve the desired behavior?
Multithreading was the missing link, in conjunction with PushRenderer and PortableRenderer (see http://wiki.icesoft.org/display/ICE/Ajax+Push+-+APIs).
I now have three threads in my backing bean- one for executing the long operation, one for polling the status, and one "main" thread for spawning the new threads and returning UI control to the client browser.
Once the main thread kicks off both execution and polling threads, it terminates and it completes the original HTTP request. My PortableRenderer is declared as PortableRender portableRenderer; and in my init() method (called by the class constructor) contains:
PushRenderer.addCurrentSession("fullFormGroup");
portableRenderer = PushRenderer.getPortableRenderer();
For the threading part, I used implements Runnable on my class, and for handling multiple threads in a single class, I followed this StackOverflow post: How to deal with multiple threads in one class?
Here's some source code. I can't reveal the explicit source code I've used, but this is a boiled-down version that doesn't reveal any confidential information. I haven't tested it, and I wrote it in gedit so it might have a syntax error or two, but it should at least get you started in the right direction.
public void init()
{
// This method is called by the constructor.
// It doesn't matter where you define the PortableRenderer, as long as it's before it's used.
PushRenderer.addCurrentSession("fullFormGroup");
portableRenderer = PushRenderer.getPortableRenderer();
}
public void someBeanMethod(ActionEvent evt)
{
// This is a backing bean method called by some UI event (e.g. clicking a button)
// Since it is part of a JSF/HTTP request, you cannot call portableRenderer.render
copyExecuting = true;
// Create a status thread and start it
Thread statusThread = new Thread(new Runnable() {
public void run() {
try {
// message and progress are both linked to components, which change on a portableRenderer.render("fullFormGroup") call
message = "Copying...";
// initiates render. Note that this cannot be called from a thread which is already part of an HTTP request
portableRenderer.render("fullFormGroup");
do {
progress = getProgress();
portableRenderer.render("fullFormGroup"); // render the updated progress
Thread.sleep(5000); // sleep for a while until it's time to poll again
} while (copyExecuting);
progress = getProgress();
message = "Finished!";
portableRenderer.render("fullFormGroup"); // push a render one last time
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
}
});
statusThread.start();
// create a thread which initiates script and triggers the termination of statusThread
Thread copyThread = new Thread(new Runnable() {
public void run() {
File someBigFile = new File("/tmp/foobar/large_file.tar.gz");
scriptResult = copyFile(someBigFile); // this will take a long time, which is why we spawn a new thread
copyExecuting = false; // this will caue the statusThread's do..while loop to terminate
}
});
copyThread.start();
}
I suggest looking at our Showcase Demo:
http://icefaces-showcase.icesoft.org/showcase.jsf?grp=aceMenu&exp=progressBarBean
Under the list of Progress Bar examples is one called Push. It uses Ajax Push (a feature provided with ICEfaces) to do what I think you want.
There is also a tutorial on this page called Easy Ajax Push that walks you through a simple example of using Ajax Push.
http://www.icesoft.org/community/tutorials-samples.jsf

Non-Blocking Endpoint: Returning an operation ID to the caller - Would like to get your opinion on my implementation?

Boot Pros,
I recently started to program in spring-boot and I stumbled upon a question where I would like to get your opinion on.
What I try to achieve:
I created a Controller that exposes a GET endpoint, named nonBlockingEndpoint. This nonBlockingEndpoint executes a pretty long operation that is resource heavy and can run between 20 and 40 seconds.(in the attached code, it is mocked by a Thread.sleep())
Whenever the nonBlockingEndpoint is called, the spring application should register that call and immediatelly return an Operation ID to the caller.
The caller can then use this ID to query on another endpoint queryOpStatus the status of this operation. At the beginning it will be started, and once the controller is done serving the reuqest it will be to a code such as SERVICE_OK. The caller then knows that his request was successfully completed on the server.
The solution that I found:
I have the following controller (note that it is explicitely not tagged with #Async)
It uses an APIOperationsManager to register that a new operation was started
I use the CompletableFuture java construct to supply the long running code as a new asynch process by using CompletableFuture.supplyAsync(() -> {}
I immdiatelly return a response to the caller, telling that the operation is in progress
Once the Async Task has finished, i use cf.thenRun() to update the Operation status via the API Operations Manager
Here is the code:
#GetMapping(path="/nonBlockingEndpoint")
public #ResponseBody ResponseOperation nonBlocking() {
// Register a new operation
APIOperationsManager apiOpsManager = APIOperationsManager.getInstance();
final int operationID = apiOpsManager.registerNewOperation(Constants.OpStatus.PROCESSING);
ResponseOperation response = new ResponseOperation();
response.setMessage("Triggered non-blocking call, use the operation id to check status");
response.setOperationID(operationID);
response.setOpRes(Constants.OpStatus.PROCESSING);
CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(() -> {
try {
// Here we will
Thread.sleep(10000L);
} catch (InterruptedException e) {}
// whatever the return value was
return true;
});
cf.thenRun(() ->{
// We are done with the super long process, so update our Operations Manager
APIOperationsManager a = APIOperationsManager.getInstance();
boolean asyncSuccess = false;
try {asyncSuccess = cf.get();}
catch (Exception e) {}
if(true == asyncSuccess) {
a.updateOperationStatus(operationID, Constants.OpStatus.OK);
a.updateOperationMessage(operationID, "success: The long running process has finished and this is your result: SOME RESULT" );
}
else {
a.updateOperationStatus(operationID, Constants.OpStatus.INTERNAL_ERROR);
a.updateOperationMessage(operationID, "error: The long running process has failed.");
}
});
return response;
}
Here is also the APIOperationsManager.java for completness:
public class APIOperationsManager {
private static APIOperationsManager instance = null;
private Vector<Operation> operations;
private int currentOperationId;
private static final Logger log = LoggerFactory.getLogger(Application.class);
protected APIOperationsManager() {}
public static APIOperationsManager getInstance() {
if(instance == null) {
synchronized(APIOperationsManager.class) {
if(instance == null) {
instance = new APIOperationsManager();
instance.operations = new Vector<Operation>();
instance.currentOperationId = 1;
}
}
}
return instance;
}
public synchronized int registerNewOperation(OpStatus status) {
cleanOperationsList();
currentOperationId = currentOperationId + 1;
Operation newOperation = new Operation(currentOperationId, status);
operations.add(newOperation);
log.info("Registered new Operation to watch: " + newOperation.toString());
return newOperation.getId();
}
public synchronized Operation getOperation(int id) {
for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
return op;
}
}
Operation notFound = new Operation(-1, OpStatus.INTERNAL_ERROR);
notFound.setCrated(null);
return notFound;
}
public synchronized void updateOperationStatus (int id, OpStatus newStatus) {
iteration : for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
op.setStatus(newStatus);
log.info("Updated Operation status: " + op.toString());
break iteration;
}
}
}
public synchronized void updateOperationMessage (int id, String message) {
iteration : for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if(op.getId() == id) {
op.setMessage(message);
log.info("Updated Operation status: " + op.toString());
break iteration;
}
}
}
private synchronized void cleanOperationsList() {
Date now = new Date();
for(Iterator<Operation> iterator = operations.iterator(); iterator.hasNext();) {
Operation op = iterator.next();
if((now.getTime() - op.getCrated().getTime()) >= Constants.MIN_HOLD_DURATION_OPERATIONS ) {
log.info("Removed operation from watchlist: " + op.toString());
iterator.remove();
}
}
}
}
The questions that I have
Is that concept a valid one that also scales? What could be improved?
Will i run into concurrency issues / race conditions?
Is there a better way to achieve the same in boot spring, but I just didn't find that yet? (maybe with the #Async directive?)
I would be very happy to get your feedback.
Thank you so much,
Peter P
It is a valid pattern to submit a long running task with one request, returning an id that allows the client to ask for the result later.
But there are some things I would suggest to reconsider :
do not use an Integer as id, as it allows an attacker to guess ids and to get the results for those ids. Instead use a random UUID.
if you need to restart your application, all ids and their results will be lost. You should persist them to a database.
Your solution will not work in a cluster with many instances of your application, as each instance would only know its 'own' ids and results. This could also be solved by persisting them to a database or Reddis store.
The way you are using CompletableFuture gives you no control over the number of threads used for the asynchronous operation. It is possible to do this with standard Java, but I would suggest to use Spring to configure the thread pool
Annotating the controller method with #Async is not an option, this does not work no way. Instead put all asynchronous operations into a simple service and annotate this with #Async. This has some advantages :
You can use this service also synchronously, which makes testing a lot easier
You can configure the thread pool with Spring
The /nonBlockingEndpoint should not return the id, but a complete link to the queryOpStatus, including id. The client than can directly use this link without any additional information.
Additionally there are some low level implementation issues which you may also want to change :
Do not use Vector, it synchronizes on every operation. Use a List instead. Iterating over a List is also much easier, you can use for-loops or streams.
If you need to lookup a value, do not iterate over a Vector or List, use a Map instead.
APIOperationsManager is a singleton. That makes no sense in a Spring application. Make it a normal PoJo and create a bean of it, get it autowired into the controller. Spring beans by default are singletons.
You should avoid to do complicated operations in a controller method. Instead move anything into a service (which may be annotated with #Async). This makes testing easier, as you can test this service without a web context
Hope this helps.
Do I need to make database access transactional ?
As long as you write/update only one row, there is no need to make this transactional as this is indeed 'atomic'.
If you write/update many rows at once you should make it transactional to guarantee, that either all rows are updated or none.
However, if two operations (may be from two clients) update the same row, always the last one will win.

Resources