0

I'm trying to implement an Alexa skill (written with Jovo) that accesses audio files that we have stored in Google's Firebase storage. However, I cannot get the lambda function to return, no matter what timeout value that I give. I believe my problem is in how I am handling async code within Lambda, but I can't figure out the correct syntax for what I am trying to do, especially within the Jovo framework.

Here's a version of some code which has the issue. In the real code it passes the url to an audio player, but this example is much simpler to demonstrate the problem:

app.setHandler({
  async LAUNCH() {
    firebase.initializeApp(firebaseConfig);
    const storageFiles = firebase.storage().ref().child('EN');
    const storageFile = storageFiles.child('1.mp3');
    const url = await storageFile.getDownloadURL();
    console.log(`My URL: ${url}`);
    this.tell(`Ok, done with getting the URL`);
  },
... More Jovo intent calls ...
});
module.exports.app = app;

When I run this in a Lambda function, I see the console.log with a good URL printed out. I also see the SSML string constructed, but I never hear the phrase and the Lambda function times out (even with a 20 sec timeout). If I comment out the getDownloadURL line, the function returns just fine. Also, if I run this outside of Lambda with jovo run, it works ok there as well.

Bottom line: what do I need to do to be able to use the getDownloadURL() method within a lambda function?

  • One update: I changed the hosting from AWS Lambda to a Google Cloud function and my program works fine. There definitely seems to be a problem with how I am trying to use getDownloadURL() specifically within a Lambda function. Since all my other Alexa/Google Home Skills are hosted in AWS, I'd really like to figure this out for Lambda. – Ben Hartman Dec 16 '19 at 11:10

2 Answers2

1

I have found a solution to my issue. The Jovo framework creates a Lambda handler function that looks like this (in index.js):

// AWS Lambda
exports.handler = async (event, context, callback) => {
    await app.handle(new Lambda(event, context, callback));
};

I needed to change the default Lambda behavior by setting the callbackWaitForEmptyEventLoop to false. The handler function now looks like:

// AWS Lambda
exports.handler = async (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    await app.handle(new Lambda(event, context, callback));
};

Once I made this change I was able to successfully get URLs from Firebase storage and play them on the Alexa audio player via a Lambda function. The default Lambda behavior is to leave certain connections open (I believe for serverless perfomance reasons), and this change modifies that behavior.

0

As console.log is shown properly the getDownloadURL() is working fine. Async fiction should return promise. I think you are missing something like return url. Please check this question for async usage: here

vitooh
  • 4,132
  • 1
  • 5
  • 16
  • Thanks for the response Witold. However, as a test, I tried removing the this.tell line and putting a 'return url' in the function, and it still does not return. I believe there is something special about using the GetDownloadURL() call inside of a lambda function. I have used other async/await calls numerous times within Lambda with no problem whatsoever. This call is somehow different - what am I missing? – Ben Hartman Dec 15 '19 at 09:29