Why does my API call not return when everything is synchronous? - rest

I have a Vue.js application on the frontend and a Node.js REST API on the backend (on AWS).
I was making a call to the backend from the front end and getting a 504 error (gateway timeout) and I couldn't figure out why. Then I copied the code from an almost identical pattern elsewhere in the application and it worked. I don't know why it worked. I'm hoping that someone can explain to me why my code didn't work and why the code I copied does work.
First you need to know what my code looks like. So here it is:
FRONTEND:
async saveTimestamp() {
try {
await api.post(`blahBlahEndpoint`);
this.flag = false;
} catch (err) {
console.log('err=', err);
}
}
BACKEND:
RequestHandler:
require('./blahBlahController').setSomething(params);
return new HttpResponse(204);
Controller:
setSomething(params) {
this.dao.setSomething(params);
}
DAO:
setSomething(params) {
this.db.runQuery('blah blah query with params');
}
So essentially, the front end sends a post request to the backend. The endpoint is our RequestHandler, and once it figures out the right controller and method for the job, it requires the controller and calls the method, passing it any parameters that came with the request. The controller in turn calls a method on the DAO passing the parameters along. The method on the DAO (setSomething(...)) injects the parameters into a query string which gets passed to db.runQuery(...). db.runQuery is from a third party library. It essentially runs the query against our database (in my case, doing a simple field update). It's asynchronous and therefore returns a promise. As you can see, I'm ignoring the promise and just returning right away (I don't need the results of the query so I just let it do its thing and carry on). So other than runQuery(...), everything is synchronous. Why then does my frontend call timeout? After about a minute of waiting, the catch block catches a 504 error, which google tells me is a timeout error.
Now let's look at the fix:
FRONTEND:
async saveTimestamp() {
try {
await api.post(`blahBlahEndpoint`);
this.flag = false;
} catch (err) {
console.log('err=', err);
}
}
BACKEND:
RequestHandler:
await require('./blahBlahController').setSomething(params);
return new HttpResponse(204);
Controller:
async setSomething(params) {
await this.dao.setSomething(params);
}
DAO:
async setSomething(params) {
await this.db.runQuery('blah blah query with params');
}
Very simple. I just converted everything on the backend to async/await. But why does this fix the problem. I'm obviously not understanding how the async/await pattern handles promises and/or how that changes the dynamic between frontend and backend (at least with REST APIs).
Here's some assumptions I'm making that may be false:
1) Without the async/await pattern (where the bug existed), all method calls from the RequestHandler to the DAO only return when the method is done, and they return nothing (with the exception of the RequestHandler which returns a HttpResponse with a 204 status to the front end).
2) Because runQuery is asynchronous, it returns a promise, and it returns it immediately (i.e. before it is done).
3) Since DAO.setSomething(...) does not await the call to runQuery, it carries on as soon as it's done calling runQuery. The promise returned from runQuery is dropped. While runQuery does its thing (updating the DB), the API begins the return portion of its round trip, returning nothing.
4) RequestHandler finally returns an HttpResponse object to the frontend. At the frontend, this object becomes the object of the Promise being awaited, effectively resolving the promise and permitting the API call to stop awaiting.
^ 4) is what doesn't seem to happen. All backend logs indicate that everything works just as I describe above, right up to the point of returning the HttpResponse, and the updates to the database corroborate this. This tells me there is an issue with how my code above (pre-fix) effects the way by which the backend returns to the frontend, but what exactly?
Here's what I imagine is happening with the fix:
1) No changes to frontend. Frontend makes call to backend like before.
2) In RequestHandler, call to setSomething(...) in controller is made asynchronously, which means it returns a promise right away and is awaited.
3) In Controller's setSomething(...), call to DAO.setSomething(...) is made. Since this is asynchronous, it returns a promise right away and is awaited.
4) In DAO.setSomething(...), call to runQuery(...) is made. Since runQuery(...) is asynchronous, it returns a promise right away and is awaited.
5) Once the promise returned from runQuery(...) is resolved (because runQuery(...) finishes), DAO.setSomething(...) returns.
6) The promise returned from DAO.setSomething(...) is resolved by DAO.setSomething(...) returning, and it stops awaiting. This causes Controller.setSomething(...) to return.
7) The promise returned from Controller.setSomething(...) is resolved by Controller.setSomething(...) returning, and RequestHandler stops awaiting.
8) RequestHandler returns the HttpResponse object to the frontend with status 204.
9) The promise returned from the api call is resolved by way of the HttpResponse object being returned. The promise resolves into the HttpResponse object. The frontend stop awaiting and continues on.
^ Hopefully, this gives a detailed account of what my understanding is surrounding frontend requests and backend responses, and what happens when promises and async/await patterns are involved. Does anyone care to correct any misunderstandings I have? Anything that would explain why the frontend wasn't getting a response in the pre-fix state?
Thanks very much for any forthcoming responses.

Related

How do I listen for an async response while doing invokeMethod()?

I am working on a small flutter app where I use a native library for some computation. The communication is two-way between dart and java (on android) and uses methodChannels for this.
I call await in_channel.invokeMethod("someJavaMethod") from dart to start the computation. This triggers an init of the native library from Java. The result from this init is coming back as an async JNI call which then triggers out_channel.invokeMethod("someDartMethod").
My plan was to bind the out_channel to a local dart broadcast stream such that I could invoke someJavaMethod and then just await myMethodStream.where((m) => m.method == "someDartMethod")...
Problem is that the "someDartMethod" can come before the "someJavaMethod" call invocation has returned.
combined code example of what I have:
static const MethodChannel _channel_in = const
MethodChannel('native_lib_wrapper_out');
static const MethodChannel _channel_out = const
MethodChannel('native_lib_wrapper_in');
final StreamController<MethodCall> _methodStreamController = new
StreamController.broadcast();
NativeLibWrapper._() {
_channel_in.setMethodCallHandler((MethodCall call) {
_methodStreamController.add(call);
return;
});
}
Future<Map<dynamic,dynamic>> initLib(String id, String filePath)
async {
Map<dynamic,dynamic> ret;
ret = await _channel_out.invokeMethod("initLib", <String,
dynamic> { // data to be passed to the function
'id': id,
'filePath': filePath,
});
print('initLib - invokeMethod done. wait for stream');
if(ret["status"] == 0) {
await NativeLibWrapper.instance._methodStream
.where((m) => m.method == "libInitEnded")
.map((m){
var args = m.arguments;
ret = args;
}).first;
}
return ret;
}
I would have expected the code to get the method call libInitEnded on my stream and then it should return after that point but it continuously hangs in the await on the stream and from the logs it looks like the libInitEnded is called before the print in the middle.
So is there a better way to structure this? it will not be the only methods going back and forth so I hope to get a good stable solution for this.
One channel
You should only need one channel. No need for in and out channels. Both ends may invoke operations on the other over the one channel.
There's only one UI thread
When you call from Dart to Native, the native method is handled by the native UI thread. Unless you are using a thread pool, that means that Dart to Native methods are handled in order. There's no point in not awaiting the answer of every native method. Or, in other words, there's no point in launching two native methods at the same time, since they will be executed consecutively by the single native thread. (Note that you should not perform time-consuming operations on the native thread, as this will interfere with other things it does like gesture detection.) Every Dart to native method should return its result.
Using a thread pool
If the single thread / single method call at a time is unacceptable, consider a thread pool at the native end. Now you can have multiple methods in flight, since there are multiple threads of execution. Now you should design your call/response like you might for communication with a server over a socket. The client gives each request an "invoke id". Each method simply returns a boolean that the request was queued. On completion of the request, the other end invokes the 'done' method, passing the original id and the result. The caller can then match up the response id with the request id and handle the response appropriately (and cancel any timer started to detect timeout). Note that responses can then arrive in any order, but are matched with their request by id.
On Android, you must invoke native to Dart methods on the UIThread. If you are calling the 'done' method from a worker thread you need to post a Runnable lambda to the main looper.

vertx timeout if async result is failed

I am seeing a timeout in the browser when the server-side service ends in a failed result. Everything works fine if the service call succeeds but it seems as though the browser never receives a response if the call fails.
My service passes a result handler to a DAO containing the following code:
final SQLConnection conn = ar.result();
conn.updateWithParams(INSERT_SQL, params, insertAsyncResult -> {
if (insertAsyncResult.failed()) {
conn.close();
resultHandler.handle(ServiceException.fail(1, "TODO"));
} else {
resultHandler.handle(Future.succeededFuture());
}
});
I'm not sure where to go from here. How do I debug what the framework is sending back to the client?
The problem was that I needed to register a ServiceExceptionMessageCodec in an intermediate Verticle, one that was sitting between the browser and the Verticle that was performing the database operation.

Scalatra: printing the HTTP status code of all APIs

I have a scalatra servlet with multiple REST APIs. For logging purposes, I use the after() method to print out the return status code after each API is called.
after() {
logger.info("request {} {} returned with status code {}", request.getMethod, request.getRequestURL, response.getStatus.toString)
}
I have noticed that when a method returns with halt, the status code is printed correctly, but when the method return a status code in the last line (without halt), the status code which will be printed will always be 200, regardless of real status returned.
For example:
post("/users/:user") {
try {
//some logic here...
if(condition)
halt(InternalServerError("DB error")) //this will cause status 500 to be printed in the 'after' method
} catch {
case e: Exception =>
InternalServerError("an unknown error occurred") //this will cause status 200 to be printed in the 'after' method
}
}
The user gets back the real status code (500) in both cases.
Any idea why this happens? Is this a bug?
I posted this question on the scalatra-user mailing list, but the list seems to be quite inactive.
Aliza
(disclaimer: I'm not a Scalatra developer but I have been using it for a project. This is based on me reading the code some time ago.)
This has to do with the way Scalatra is handling thrown exceptions (the relevant code seems to be start from this one). If the somewhere in runActions an exception is thrown (halt throws a HaltException), the catch block of cradleHalt will be called and we go to renderHaltException which will set the response status code.
It's not exactly the same when you're not calling halt but returns an ActionResult directly. In that case, executeRoutes seems to produce a value which is then passed on to renderResponse, which will then call renderResponseBody and finally the renderPipeline. This block seems to be the place where the actual status code from an ActionResult is actually set. However, the after function is already called (it was called in actionResult, before executeRoutes returns). So what you get is exactly your behavior: when you don't use halt, the correct response status is only set in the actual response but not your logging call.
You've probably tried this, but the quick fix to your InternalServerError not generating the correct HTTP status code when logged is to simply wrap it in another halt call.
As to whether this is a bug, I can't say. I'm guessing probably not, because they did say in the executeRoutes documentation that after is called before the actionResult is passed to renderResponse. What was not clear was that the act of rendering actionResult also sets the HTTP error code, which you wanted to log before.
You'll have to confirm with them on that :).

Empty response on long running query SailsJS

I'm currently running SailsJS on a Raspberry Pi and all is working well however when I execute a sails.models.nameofmodel.count() when I attempt to respond with the result I end up getting a empty response.
getListCount: function(req,res)
{
var mainsource = req.param("source");
if(mainsource)
{
sails.models.gatherer.find({source: mainsource}).exec(
function(error, found)
{
if(error)
{
return res.serverError("Error in call");
}
else
{
sails.log("Number found "+found.length);
return res.ok({count: found.length});
}
}
);
}
else
{
return res.ok("Error in parameter");
}
},
I am able to see in the logs the number that was found (73689). However when responding I still get an empty response. I am using the default stock ok.js file, however I did stick in additional logging to try to debug and make sure it is going through the correct paths. I was able to confirm that the ok.js was going through this path
if (req.wantsJSON) {
return res.jsonx(data);
}
I also tried adding .populate() to the call before the .exec(), res.status(200) before I sent out a res.send() instead of res.ok(). I've also updated Sails to 11.5 and still getting the same empty response. I've also used a sails.models.gatherer.count() call with the same result.
You can try to add some logging to the beginning of your method to capture the value of mainsource. I do not believe you need to use an explicit return for any response object calls.
If all looks normal there, try to eliminate the model's find method and just evaluate the request parameter and return a simple response:
getListCount: function(req, res) {
var mainsource = req.param("source");
sails.log("Value of mainsource:" + mainsource);
if (mainsource) {
res.send("Hello!");
} else {
res.badRequest("Sorry, missing source.");
}
}
If that does not work, then your model data may not actually be matching on the criteria that you are providing and the problem may lie there; in which case, your response would be null. You mentioned that you do see the resulting count of the query within the log statement. If the res.badRequest is also null, then you may have a problem with the version of express that is installed within sailsjs. You mention that you have 11.5 of sailsjs. I will assume you mean 0.11.5.
This is what is found in package.json of 0.11.5
"express": "^3.21.0",
Check for any possible bugs within the GitHub issues for sailsjs regarding express and response object handling and the above version of express.
It may be worthwhile to perform a clean install using the latest sailsjs version (0.12.0) and see if that fixes your issue.
Another issue may be in how you are handling the response. In this case .exec should execute the query immediately (i.e. a synchronous call) and return the response when complete. So there should be no asynchronous processing there.
If you can show the code that is consuming the response, that would be helpful. I am assuming that there is a view that is showing the response via AJAX or some kind of form POST that is being performed. If that is where you are seeing the null response, then perhaps the problem lies in the view layer rather than the controller/model.
If you are experiencing a true timeout error via HTTP even though your query returns with a result just in time, then you may need to consider using async processing with sailjs. Take a look at this post on using a Promise instead.

Restangular - how to cancel/implement my own request

I found a few examples of using fullRequestInterceptor and httpConfig.timeout to allow canceling requests in restangular.
example 1 | example 2
this is how I'm adding the interceptor:
app.run(function (Restangular, $q) {
Restangular.addFullRequestInterceptor(function (element, operation, what, url, headers, params, httpConfig) {
I managed to abort the request by putting a resolved promise in timeout (results in an error being logged and the request goes out but is canceled), which is not what I want.
What I'm trying to do - I want to make the AJAX request myself with my own requests and pass the result back to whatever component that used Restangular. Is this possible?
I've been looking a restangular way to solve it, but I should have been looking for an angular way :)
Overriding dependency at runtime in AngularJS
Looks like you can extend $http before it ever gets to Restangular. I haven't tried it yet, but it looks like it would fit my needs 100%.
I'm using requestInterceptor a lot, but only to change parameters and headers of my request.
Basically addFullRequestInterceptor is helping you making change on your request before sending it. So why not changing the url you want to call ?
There is the httpConfig object that you can modify and return, and if it's close to the config of $http (and I bet it is) you can change the url and even method, and so change the original request to another one, entirely knew.
After that you don't need timeout only returning an httpConfig customise to your need.
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params, httpConfig) {
httpConfig.url = "http://google.com";
httpConfig.method = "GET";
httpConfig.params = "";
return {
httpConfig: httpConfig
};
});
It will be pass on and your service or controller won't know that something change, that's the principle of interceptor, it allow you to change stuff and returning to be use by the next process a bit like a middleware. And so it will be transparent to the one making the call but the call will be made to what you want.