Lazy Listener for Bolt Python not doing what it is supposed to
sinjachen opened this issue · comments
I am trying to create a slack app that involves querying data from another service through their API and trying to host it on AWS lambda. Since querying data from another API is going to take quite a long time and slack has the 3 second time-out I've been trying to use the lazy listener, but even just copy pasting from the documentation does not really work out. What happens is that the acknowledgement goes through, but then the actual message that gets delayed by 5 seconds is not being sent. Obviously the app itself is going to be more complicated than this, but currently I am just trying to get the most basic thing to run.
Reproducible in:
pip freeze | grep slack
python --version
sw_vers && uname -v # or `ver`
The slack_bolt
version
slack-bolt==1.18.1
slack-sdk==3.26.1
Python runtime version
Python 3.10.0
OS info
ProductName: macOS
ProductVersion: 14.0
BuildVersion: 23A344
Darwin Kernel Version 23.0.0: Fri Sep 15 14:41:34 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T8103
Steps to reproduce:
This is the current script I am using for my lambda function:
import os
import logging
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
import time
# process_before_response must be True when running on FaaS
app = App(process_before_response=True,
signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
token=os.getenv("SLACK_BOT_TOKEN"))
def respond_to_slack_within_3_seconds(body, ack):
text = body.get("text")
if text is None or len(text) == 0:
ack(":x: Usage: /testing this is the acknowledgement to slack")
else:
ack(f"Accepted! (task: {body['text']})")
logging.info(body)
logging.info('ack() done')
pass
def run_long_process(respond, body):
time.sleep(5) # longer than 3 seconds
respond(f"Completed! (task: {body['text']})")
logging.info(body)
logging.info('long run with 5 sec delay done.')
app.command("/testing")(
ack=respond_to_slack_within_3_seconds, # responsible for calling `ack()`
lazy=[run_long_process] # unable to call `ack()` / can have multiple functions
)
SlackRequestHandler.clear_all_log_handlers()
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG)
def lambda_handler(event, context):
slack_handler = SlackRequestHandler(app=app)
logging.info(event)
logging.info(context)
return slack_handler.handle(event, context)
Expected result:
I expect to receive one message informing me that my slash command was acknowledged and another one with the actual response.
Actual result:
Over slack I only receive the message that my command was acknowledged and no matter how long I wait the actual response does not come through.
Furthermore, when I check the logs, I can also see there that my command was acknowledged but despite that the function still times out.
This is also the first time I am doing anything in this direction, so any help would be appreciated.
Requirements
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
Hi @sinjachen thanks for writing in 💯
Setting the process_before_response
flag as True
will simply holds off on sending an HTTP response until all the things in a listener function are done this does not change Slacks 3 second acknowledgement timeout. In other words you still need to complete your processing within 3 seconds or you will run into errors with Slack timeouts.
If you want to use FaaS for long running processes I would recommend using some sort of message broker solution like kafka, google sub/pub, amazon mq to handle these long running processes
Let me know if this helps you on your journey 🗺️
Hi @WilliamBergamin
thanks for your fast response. What will it do if I set process_before_response to false? Is there a simple way to solve this issue? My understanding was that the purpose of the lazy listener function was so that longer running processes can be implemented. In my case these would take 3-4 minutes max. I'm trying to avoid having to use other services.
The purpose of process_before_response
was introduced for the concept of lazy listeners that are used when running your app on FaaS or similar runtimes which do not allow you to run threads or processes after returning an HTTP response
With process_before_response
set to false, calling ack()
instructs the bolt framework to return an HTTP response, in theory this should also complete the FaaS process. When you set process_before_response
to true this instructs bolt to delay any HTTP response until the listener completes but if the listener takes more the 3 seconds to complete you will get a Slack timeout error
If you want to keep your solution as simple as possible you can deploy it on a traditional stateful solution like an EC2 instance in this scenario you will be able to send an HTTP response as soon as you receive a request from Slack and then continue the process that could take 3-4 min
The "Task timed out after xx seconds" error indicates your default max duration for AWS Lambda function execution might be 3 seconds. I'd suggest adjusting the setting to allow longer execution. AWS Lambda allows up to 15 minutes. See also: #630 (comment)
Thank you so much! That was it. It's working now :)
Great to hear!