I'm using Cloud Functions for Firebase to:
Receive parameters from api.ai
Make a call to a third-party API and
Respond back to api.ai.
My call to the third-party API uses the request Node.js module and is wrapped within a function (getInfoFromApi()) in index.js.
The problem I'm having is that the execution of the secondary function call is consistently taking between 15-20 seconds. Note: The cloud function itself completes its execution consistently in the 400 ms range.
By logging simple comments to the console I can see when the function starts, when the secondary function is being called and when it receives a response from the third party, so I think I can see what's happening.
Roughly, the timings look like this:
0: cloud function initialises
400 ms: cloud function completes
16 s: getInfoFromApi() function is called (!)
17 s: third-party API returns results
My questions:
Is there an obvious reason for the delay in calling the secondary function? This doesn't seem to be caused by the cold start issue since the cloud function springs to life quickly and the delay is consistent even after repeated calls.
Is the use of the 'request' node module causing the issue? Is there a better module for creating/managing http requests from cloud functions?
You can see a simplified Gist of the index.js here: https://gist.github.com/anonymous/7e00420cf2623b33b80d88880be04f65
Here is a grab of the Firebase console showing example timings. Note: the output is slightly different from the above code as I simplified the above code to help understanding.
The getInfoFrom3rdParty() call is an asynchronous event. However, you haven't returned a promise from your function, so Functions is not waiting for the async event to complete.
It looks to me like, since you are returning undefined, the Function also assumes that it failed and retries. At some point in the retry process, the async event is probably completing before the function exits (i.e. unintentional success due to race conditions). I've seen similar outcomes in other cases where users don't return a promise or value in their functions.
I can't tell from the gist what you're trying to do--it doesn't appear to actually do anything with the third party results and probably isn't a realistic mcve of your use case. But something like this is probably what you want:
exports.getInfo = functions.https.onRequest((request, response) => {
// ....
// NOTE THE RETURN; MOST IMPORTANT PART OF THIS SAMPLE
return getInfoFromThirdParty(...).then(() => {
response.writeHead(200, {"Content-Type": "application/json"});
response.end(JSON.stringify(payload));
}).catch(e => /* write error to response */);
});
function getInfoFrom3rdParty(food) {
reqObj.body = '{"query": "'+food+'"}';
return new Promise((resolve, reject) => {
mainRequest(reqObj, function (error, response, body) {
// ....
if( error ) reject(error);
else resolve(...);
// ....
});
});
}
From my experience with cloud functions, once the "execution finish" flag completes, it will then cause a delay (from 3s up to 15s). This blocks the remaining execution and increases your total response time.
To overcome this, you could try placing a Promise to your 3rd party call, once it completes, then do the "response.send()" which ends the function.
Related
I have a firebase function that does lot of checks in the firebase realtime database and then returns a response.
Does the node runtime in firebase garantee that the async functions will be executed in the order they are call? or there is some sort of non fifo scheduler that executes then?
the logic of the real function is a bit complex (over 200 lines) so to avoid the extra complexity i will just use a pseudo function as example:
function checks(req,res){
let resp;
database.ref('nodeA').once('value').then(function(data) {
//do some checks and modify resp
});
database.ref('nodeB').once('value').then(function(data) {
//do some checks and modify resp
});
database.ref('nodeC').once('value').then(function(data) {
//do some checks and modify resp
res.status(200).send(resp);
});
FIRST OF ALL. I know I can make nested calls to the realtime database and garantee the execution of all checks, but my real case scenario is more complex than this and would't work for me
Is there any garantee that all checks will be executed by this sample code?
if not... how can i make a non blocking while that waits it to be ready?
like:
while(!resp.ready){
wait //how to wait without blocking the other functions
}
res.status(200).send(resp);
In the code you shared the order in which the three requests are sent to the database is in the order you specify them. The results are also guaranteed to come in that same order.
So by the time nodeC is loaded, it is guaranteed that the first two callbacks have also been invoked.
try async and await for this case,
in your code, you will send the response to the user before finishing all the validation, there is no guarantee the callback function for each promise will execute in the same order.
async function checks(req,res){
let resp;
let nodeAData=await database.ref('nodeA').once('value');
//do some checks and modify resp
let nodebData=database.ref('nodeB').once('value')
//do some checks and modify resp
.
.
.
res.status(200).send(resp);
});
I'm experimenting with the translation service on the Microsoft bot framework. I've written a method to which I pass a callback function which receives my translated text.
I've got an existing bot that calls an HTTP endpoint to create my output in English. I want to translate the output to the different language before returning it to the user. My unaltered code looks like this:
await request.post(ENDPOINT,
{
headers: HEADERS,
json: BODY
},
async function (error, response, body) {
if (response.statusCode == 202) {
var msg = body.mainResponse.text;
context.sendActivity(msg);
}
});
This runs just fine. Data passed in the HTTP response body gets parsed sent back to the user.
Now I want to plug in my translation service. I've got a single function that I call to do this called Translator.translate(text, callback). I've added this call to my existing function to get:
await request.post(ENDPOINT,
{
headers: HEADERS,
json: BODY
},
async function (error, response, body) {
if (response.statusCode == 202) {
var msg = body.mainResponse.text;
await Translator.translate(msg, function (output) {
context.sendActivity(output);
});
}
}
);
My translation process runs and I get the translation in the output variable, but nothing gets sent back to the user. Looking at the terminal, I see the error "Cannot perform 'get' on a proxy that has been revoked" relating to the context.sendActivity line in my callback.
Can anyone suggest how I keep the context object active?
Thanks in advance.
Many thanks for the assistance everyone - I never completely got to the bottom of this, but I finally fixed it with a complete re-write of the code. I think the problem was caused by a large number of nested synchronous and asynchronous calls. My ultimate solution was to completely get rid of all the nesting - first calling the translation service (and waiting for it), then doing the original call.
I think there are a number of other asynchronous threads inside the methods of both pieces of functionality. I don't have a great understanding of how this works in node, but I'm guessing that the response was getting popped off the stack at the wrong point, which is why I wasn't seeing it. The "cannot perform get" error was a bit of a red herring, it turns out. I get the same error from some of Microsoft's working demo code. I'm sure there's a separate issue there that ought to be fixed, but it wasn't actually caused by this issue. The code was running, but the output was getting lost.
I'm learning node now and I'm confused about the err parameter.
I thought it's supposed to be the first argument of a callback function but I don't see it in many call back functions. Can anyone explain it to me? Thanks!
There's many different kinds of functions and callback functions in particular. The Node.js standard for callback functions is those of the form:
function(err, arg1, arg2, ...)
Where arg1 and so forth are only present if relevant but the err argument is always first. This is the reverse of a lot of historical JavaScript code where errors would be the last argument.
The Node.js method of forcing the error as the first argument even if there's no error makes ignoring errors harder, you rarely forget to declare that argument, and makes their location predictable.
Now this only applies in the case of a general-purpose callback. That is, there are occasions where calling a function will trigger a singular callback at some point in the future. You'll see them used like this:
doStuff(function(err, successValue) { ... });
There's also the style popularized by jQuery where one or more of your callbacks will be triggered depending on the outcome of the operation:
doStuff({
success: function(successValue) { ... },
error: function(err) { ... },
timeout: function() { ... }
});
Note that in this case you may have both the error and timeout callbacks being fired. You're not obligated to populate all of these, either.
The downside to this approach is the unpredictability of which ones get called and the risk of handling something twice inadvertently.
The error parameter is usually for asynchronous code.
node errors
Most asynchronous methods that accept a callback function will accept an Error object passed as the first argument to that function. If that first argument is not null and is an instance of Error, then an error occurred that should be handled.
app.get() sends get request and return an error like a 404
and you could do something like this res.status(404).render( in app.get()
Express error handling
error-handling functions have four arguments instead of three: (err, req, res, next)
The reason why some code uses err as the first parameter is because some code like fs.readFileis programmed to check if there was an error and to handle it. The author of the API specifically wrote code to check the first argument for an error and handle it.
That's why it is available to you for some methods an unavailable for other methods.
First: a callback is just a function. Different callbacks serve different purposes.
In general, a function that performs an asynchronous action and should "return" a value gets passed a callback function that will take (at least) two arguments: the first is used to pass errors (if any), the second (and following) are used to pass the value(s) that should be returned to the caller.
You noticed that net.createServer() will also take a callback function, but that function only has one argument.
That's because, in this case, the callback isn't used to pass errors and/or values. Instead, it's a function that gets called when a new connection is made to the server.
It's really a bit of a shortcut. This code:
var server = net.createServer(function(connection) {
...
});
Is short for this code:
var server = net.createServer();
server.on('connection', function(connection) {
...
});
I'm still learning the node.js ropes and am just trying to get my head around what I should be deferring, and what I should just be executing.
I know there are other questions relating to this subject generally, but I'm afraid without a more relatable example I'm struggling to 'get it'.
My general understanding is that if the code being executed is non-trivial, then it's probably a good idea to async it, as to avoid it holding up someone else's session. There's clearly more to it than that, and callbacks get mentioned a lot, and I'm not 100% on why you wouldn't just synch everything. I've got some ways to go.
So here's some basic code I've put together in an express.js app:
app.get('/directory', function(req, res) {
process.nextTick(function() {
Item.
find().
sort( 'date-modified' ).
exec( function ( err, items ){
if ( err ) {
return next( err );
}
res.render('directory.ejs', {
items : items
});
});
});
});
Am I right to be using process.nextTick() here? My reasoning is that as it's a database call then some actual work is having to be done, and it's the kind of thing that could slow down active sessions. Or is that wrong?
Secondly, I have a feeling that if I'm deferring the database query then it should be in a callback, and I should have the actual page rendering happening synchronously, on condition of receiving the callback response. I'm only assuming this because it seems like a more common format from some of the examples I've seen - if it's a correct assumption can anyone explain why that's the case?
Thanks!
You are using it wrong in this case, because .exec() is already asynchronous (You can tell by the fact that is accepts a callback as a parameter).
To be fair, most of what needs to be asynchronous in nodejs already is.
As for page rendering, if you require the results from the database to render the page, and those arrive asynchronously, you can't really render the page synchronously.
Generally speaking it's best practice to make everything you can asynchronous rather than relying on synchronous functions ... in most cases that would be something like readFile vs. readFileSync. In your example, you're not doing anything synchronously with i/o. The only synchronous code you have is the logic of your program (which requires CPU and thus has to be synchronous in node) but these are tiny little things by comparison.
I'm not sure what Item is, but if I had to guess what .find().sort() does is build a query string internally to the system. It does not actually run the query (talk to the DB) until .exec is called. .exec takes a callback, so it will communicate with the DB asynchronously. When that communication is done, the callback is called.
Using process.nextTick does nothing in this case. That would just delay the calling of its code until the next event loop which there is no need to do. It has no effect on synchronicity or not.
I don't really understand your second question, but if the rendering of the page depends on the result of the query, you have to defer rendering of the page until the query completes -- you are doing this by rendering in the callback. The rendering itself res.render may not be entirely synchronous either. It depends on the internal mechanism of the library that defines the render function.
In your example, next is not defined. Instead your code should probably look like:
app.get('/directory', function(req, res) {
Item.
find().
sort( 'date-modified' ).
exec(function (err, items) {
if (err) {
console.error(err);
res.status(500).end("Database error");
}
else {
res.render('directory.ejs', {
items : items
});
}
});
});
});
I'm using the async library to help me with my control flow. I have a collection over which I want to iterate, for each element execute 1 asynchronous task and when all are done, call the callback.
I've decided to use an async.forEach loop, on each loop I call my asynchronous task but I get an error: callback was already called, but shouldn't the callback be called only when all callbacks are called? And I even wanted to understand properly how to handle errors, it is highly probable that some task will fail and others will succeed, I don't need to know which elements fail, but I would like, how can I do this?
This is my code:
async.forEach(fonts, function(font, callback) {
ftpm_module.installOsFont(font, callback);
}, function() {
console.log("finished");
});
EDIT: the error occurs only if I pass 2 or more fonts.