slackapi / bolt-js

A framework to build Slack apps using JavaScript
https://slack.dev/bolt-js
MIT License
2.74k stars 393 forks source link

Bot replies twice when Lambda cold starts #816

Closed jc1518 closed 3 years ago

jc1518 commented 3 years ago

Description

I have setup a Slack Bolt on AWS Lambda by following this instruction - https://slack.dev/bolt-js/deployments/aws-lambda#set-up-aws-lambda

I want the Bot to reply 'hi' if someone says 'help'. Sample code:

app.message('help', async ({ message, say }) => {
  await say(`hi`);
});

I noticed that whenever the Lambda is cold, the Bot always says hi twice. But when Lambda is warm, the Bot only replies once. What is the possible solution to prevent the Bot from answering it twice when Lambda cold starts. Thanks!

What type of issue is this? (place an x in one of the [ ])

Requirements (place an x in each of the [ ])


Bug Report

Filling out the following details about bugs will help us solve your issue sooner.

Reproducible in:

package version:

        "@slack/bolt": "^3.2.0",
        "@vendia/serverless-express": "^4.3.3",

node version: v12.14.1

Environment: AWS Lambda

mwbrooks commented 3 years ago

Hey @jc1518 πŸ‘‹πŸ»

Thanks for the details, especially including the code sample and package versions!

The AWS Lambda/Slack API cold start issue can be frustrating because Slack requires a 3 second response time.

Just to make sure, have you already added the processBeforeResponse flag to your ExpressReceiver?

// Initialize your custom receiver
const expressReceiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  // The `processBeforeResponse` option is required for all FaaS environments.
  // It allows Bolt methods (e.g. `app.message`) to handle a Slack request
  // before the Bolt framework responds to the request (e.g. `ack()`). This is
  // important because FaaS immediately terminate handlers after the response.
  processBeforeResponse: true
});
jc1518 commented 3 years ago

Hi @mwbrooks ,

Thanks for the quick response. Yes, I have the processBeforeResponse: true settings. I am thinking this issue could be caused by the Lambda concurrent execution? When Lambda cold starts, it may trigger more than one execution.

jc1518 commented 3 years ago

I changed the limit of the concurrent execution to 1, and the Bot only replies once now when Lambda cold starts.

seratch commented 3 years ago

@jc1518 You can check the initialization time in CloudWatch Logs. How long does it take for cold-starts?

Bolt itself is a relatively small package. As long as a function built with Bolt does not have any other dependencies, cold-starts cannot be an issue. Thus, I'm guessing some other packages may take a bit long for their initialization.

I don't have the past examples with Node but let me share two examples in other runtimes:

In your case, the cause of slow bootstrapping time can be a difference module or code. I'd suggest checking the actual spent time and trying to figure the major causes out step by step.

jc1518 commented 3 years ago

Hi @seratch,

Yes, it has other package as well which may contribute to the slowness. I am thinking to split the Lambda function into multiple smaller ones to keep the main one light. All the main one does is to ack to Slack and forward the message to EventBridge. Other lambdas functions that takes long time to process will be triggered by the events from the main one. Does it sound right to you? Thanks.

seratch commented 3 years ago

@jc1518 Yes, your idea sounds a great approach to me πŸ‘

seratch commented 3 years ago

@jc1518 Thanks for your πŸ‘ on my comment. It seems we're done with this topic. Let me close this issue now.

anandbaburajan commented 2 years ago

Yes, it has other package as well which may contribute to the slowness. I am thinking to split the Lambda function into multiple smaller ones to keep the main one light. All the main one does is to ack to Slack and forward the message to EventBridge. Other lambdas functions that takes long time to process will be triggered by the events from the main one. Does it sound right to you? Thanks.

@jc1518 did that resolve your issue? If no, can you please share what approach you finally took? Thanks!

anandbaburajan commented 2 years ago

I did something like this to deal with the Lambda cold start time-out error issue:

const dispatcher = async (event, context) => {
  const lambda = new AWS.Lambda();

  return await lambda.invoke({
    FunctionName: mainEventHandlingLambda,
    InvocationType: "Event",
    Payload: JSON.stringify(event)
  }).promise().then(() => {
    return { statusCode: 200 };
  });
};
seratch commented 2 years ago

Hi @AnandBaburajan, thanks for sharing your knowledge! Your solution works for Events API etc. without any problems. However, in the following situations, it may not fully function because your app has to synchronously return data in these scenarios:

I hope this was helpful to you and others!

anandbaburajan commented 2 years ago

Hi @seratch, thank you for your helpful comments, and more thanks for building Bolt and providing helpful docs and examples! Our app doesn't come under those three scenarios - we're using the Calls API and using the response URL to send a message. But the comment is definitely helpful and we'll keep the points in mind when we think about expanding our app. Thanks again!