Initial response from Dialogflow fulfillment library takes very long (30 sec) - node.js

Using the dialogflow-fulfillment-nodejs library for connecting Dialogflow to the Zendesk web widget, I experience a very long delay (about 30 sec) only with the initial request e.g. by just entering "hello" to trigger Default Welcome intent. All subsequent requests are fulfilled immediately.
This occurs with a custom Node.js script deployed on Firebase, it does not happen when testing under localhost using the Firebase emulator.
I seem not to be able to obtain any useful logging output from Firebase in order to understand where the delay is coming from. The delay happens after calling sessionClient.detectIntent(request):
console.timeLog("process", "Dialogflow Request");
const responses = await sessionClient.detectIntent(request);
console.timeLog("process", "Dialogflow Response");//continues 30 seconds later at very first request
The only observation I have is that the request arrives this late in the onRequest function:
exports.fulfillment = functions.https.onRequest((request, response) => {
console.log("onRequest Function triggered");//happens after 30 secs
//...
})
There is no error shown in the Firebase logs, and the onRequest fulfillment function finishes successfully after a few ms.
I would be thankful for getting some hints on how to troubleshoot this issue since I am currently out of ideas.

Related

Delay koa request 30 minutes but send 200 header immediately

I'm looking to delay all requests to an endpoint 30 minutes from when it was originally received. I'm working with Shopify and am responding to an order creation webhook, passing along data to another 3rd party service.
This is what happens: an order is placed, and then a webhook fires via Shopify, which contacts my koa endpoint with the associated order data.
I want to receive the webhook body (which includes order data) immediately, but then wait 30 minutes, then send along that information to another service. But I want to send a 200 header back to Shopify immediately, regardless of whether or not the code executes properly after the 30 minute delay (if it doesn't, I'll be informed via an error email anyway and can handle it manually, it wouldn't be the end of the world).
I've tried setTimeout methods, but the issue seems to be this: I can't send a 200 header back to Shopify until after the 30 minutes is up (or I shouldn't anyway, according to Koa docs?).
Meaning, if I do something like
context.response.status = 200; // tried to send response code immediately due to 30 minute timeout.
await handleRequest(context.request.body); // code waits 30 mins via settimeout
the 200 header isn't being sent right away
By the time 30 minutes has passed, Shopify has long given up on receiving a 200 header and has automatically put the request in a retry queue, which means the webhook will fire again before the 30 minutes is up, so I'll be sending duplicate requests to the service I'm trying to wait 30 minutes for.
I've considered cron, but it's not that I want these to be processed every 30 minutes, I want each individual request to wait 30 minutes to send to a third party service after it was received.
I'm really trying to avoid a database.
I'm fine with using setTimeout, if there is a way to send a 200 header back to Shopify before the timeout and subsequent service call after 30 minutes.
Status code goes with the first part of the HTTP message, so you need to start sending, or end the HTTP response.
In this case, you don't need to block the response on the 3rd party call. Move the logic you want to run in 30 minutes into its own function and don't await on the timer. You don't need it in your response logic.
Some pseudo code:
function talkTo3rdPartyDelayed(context, delay) {
setTimeout(()=>{
// talk to 3rdparty
}, delay);
return; // continue what you were doing before, this is non-blocking
}
function listenForShopify(req, res, next) {
talkTo3rdPartyDelayed({req.body}, 30*60*1000);
res.statusCode = 200;
next(); // or res.end() which sends the status code
}
As an aside, in a robust system, you'd store that 3rd party request you want delayed for 30 minutes in a queue system, with a visibility timer set to 30 minutes ahead. That way, if the process dies, you don't lose that work you intended to do. Having to manually handle any issues that come up will get tiresome.

Slow time response on DialogFlow fullfilment http requests

I am developing an app for google assistant on DialogFlow.
On certain intent I have a fullfilment which has to do a http request.
The code is like this:
const syncrequest = require('sync-request');
console.log('Request start');
var res = syncrequest('GET', urlRequest, {
json: {},
});
console.log('Request end');
Testing the url that I'm using it takes approximately 0.103 seconds to respond.
But looking at the firebase log, it is like this:
3:01:58.555 PM dialogflowFirebaseFulfillment Request end
3:01:56.585 PM dialogflowFirebaseFulfillment Request start
Even thought my server respond in 0.103 seconds, the request takes 2 seconds to be processed.
Sometimes it takes more than 4 seconds and makes my app crash.
Does anyone have any idea why is it taking so long? Is there something that I can do to do the request faster?
Thanks in advance
I haven't looked too hard at the sync-request package, but I do see this big warning on the npm page for it:
You should not be using this in a production application. In a node.js
application you will find that you are completely unable to scale your
server. In a client application you will find that sync-request causes
the app to hang/freeze. Synchronous web requests are the number one
cause of browser crashes. For production apps, you should use
then-request, which is exactly the same except that it is
asynchronous.
Based on this, and some other information on the page, it sounds like this package is very poor on performance, and may handle the synchronous operations grossly inefficiently.
You may wish to switch to the then-request package, as it suggests, however the most common way to handle HTTP calls is using request-promise-native, where you'd do something like:
const rp = require('request-promise-native');
return rp.get(url)
.then( body => {
// Set the Dialogflow response here
// You didn't really show this in your code.
});
If you are doing asynchronous tasks - you must return a promise from your intent handler.

expressjs sending err_empty_response

I have a problem with my expressJS framework when I am sending a response with delay 200 seconds it's sending err_empty_response with status code 324
here is fakeTimer example
fakeTimeout(req, res) {
setTimeout(() => { res.json({success: true})}, 200000)
}
ERR_EMPTY_RESPONSE is a Google Chrome error code.
Actually, Chrome will automatically timeout requests when they exceeds 300 seconds, and there is no way to change that settings unfortunately.
One workaround could be to change the Keep Alive headers.
However, if one task is taking longer than one minute, you should really just don't let the user wait that amount of time and have a feedback later on the UI when it's completed.

the right way to return a response from firebase a Google Cloud Pub/Sub Triggers

I am trying to implement a firebase cron function from the functions-cron example
var functions = require('firebase-functions');
exports.hourly_job =
functions.pubsub.topic('hourly-tick').onPublish((event) => {
console.log("This job is ran every hour!")
});
and I just wants to know the right way to return 200 response as it s requested because I have the error log
Function returned undefined, expected Promise or value
since I dont have access to response object as in the HTTP triggers I just want to know if returning the 200 int value is sufficient ?
the doc states the following
Cron retries
If a cron job's request handler returns a status code that is not in the range 200–299 (inclusive) App Engine considers the job to have failed. By default, failed jobs are not retried. You can cause failed jobs to be retried by including a retry_parameters block in your configuration file.
Pub/Sub triggers don't have a response. They simply receive messages as they appear. Only HTTPS triggers require a response sent to the client.
If you want to prevent that warning message, simply return null at the end of your function as you show it now. The actual return value is meaningless. If you're doing async work in the function, you should instead return a promise that's resolved when the work is complete.

Why does my Lambda function time out even though the API Gateway callback has already been called?

I have an AWS API Gateway method that proxies requests through to AWS Lambda. However, it errors after three seconds with the following in the logs:
Endpoint response body before transformations: {"errorMessage":"2017-09-05T16:30:49.987Z 922186c0-9257-11e7-9db3-51921d5597a2 Task timed out after 3.00 seconds"}
Thus, I went on to check my Node 6.10 AWS Lambda function to see why it was timing out. I added logging statements before and after every function call. Surprisingly, it did everything it's supposed to do: called the API Gateway callback, and run a query against the database after that. All that takes 0.6s, and as far as I'm aware there's no other code left to run. Nevertheless, it appears to keep on running for the rest of the three seconds and then timing out. (This is, I think, because I'm leaving a connection to the database open.)
The logs statements I placed before and after the callback call indicate that the that call is executed in under half a second. Yet, that response doesn't seem to make it to API Gateway, whereas the error after three seconds does.
What could be potential reasons for this, and how can I debug it?
By default calling the callback() function in a NodeJS Lambda function does not end the function execution. It will continue running until the event loop is empty. A common issue with NodeJS Lambda functions continuing to run after callback is called occurs when you are holding on to open database connections. You haven't posted any code, so I can't give specific recommendations, but you would need to determine if you are leaving database connections open in your code or something similar.
Alternatively, you can change the behavior such that the execution ends as soon as the callback function is called by setting callbackWaitsForEmptyEventLoop = false on the context object.
Your API gateway has a fixed timeout of 29 seconds. Most queries do complete within this timeframe.
Increase your lambda execution timeout to anywhere between 30 Sec to 3 Min 00 Sec.
Use context.succeed() instead of callback.
This worked for me.
const mysql = require('mysql');
const connection = mysql.createConnection({
host : 'your_mysql_host',
user : 'your_mysql_user',
password : 'your_mysql_password',
database : 'your_mysql_db'
});
exports.handler = (event, context) => {
var userId = event.params.querystring.userid;
const sql = 'SELECT * FROM users where USER_ID=' + userId;
var response = {
"statusCode": 200,
"body": "body_text_goes_here"
}
if(userId){
connection.query(sql, function (error, results, fields) {
if (error) {
context.succeed(error);
} else {
response.body = results;
context.succeed(response);
}
});
}
}
I had same issue and I've updated the timeout in my code but no luck finally increased lambda execution time that fixed my problem.
How to increase an AWS Lambda timeout?

Resources