Search code examples
pythonflasktelegram-botgoogle-cloud-runpython-telegram-bot

Telegram bot (Flask app on Google Cloud Run) stops responding after instance reloading


I'm trying to make my first Telegram bot on Python. I use the python-telegram-bot, Flask, run it in Google Cloud Run. Locally on my machine everything works fine, when I deploy it (using Docker) to Google Cloud Run everything also works fine until the moment when Google Cloud Run stops the instance. That's what I see in Gloud Run logs:

[2022-02-23 11:09:24 +0000] [1] [INFO] Handling signal: term
[2022-02-23 11:09:24 +0000] [3] [INFO] Worker exiting (pid: 3)
[2022-02-23 11:09:25 +0000] [1] [INFO] Shutting down: Master

After this bot stops responding.

I tried to set up a min inctances feature (set a min-instances=1) but it didn't help, bot stops responding after a while.

This is a part of code from main.py:

...
@app.route("/")
def main() -> None:
    updater = Updater("TOKEN")
    dispatcher = updater.dispatcher
    conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', start), CommandHandler('menu', menu), CallbackQueryHandler(button)],
        states={
            START: [
                MessageHandler(Filters.regex('^(Default Text)$') & (~ Filters.command), start)
            ]},
        fallbacks=[CommandHandler('cancel', cancel), CommandHandler('menu', menu)],
        allow_reentry=True
    )

    dispatcher.add_handler(conv_handler)
    updater.start_polling()
    updater.idle()


if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

I guess the problem is that the bot starts only when I make GET request to my Google Cloud URL: app.route("/") is triggered and the main function is started. If the Cloud Run instance stops and I manually request my service URL (that I get from Google Cloud Run, like https://service-name-xxxxx-xx.a.run.app), the bot starts up again. This is a logs from Cloud Run after this:

[2022-02-23 11:09:24 +0000] [1] [INFO] Handling signal: term
[2022-02-23 11:09:24 +0000] [3] [INFO] Worker exiting (pid: 3)
[2022-02-23 11:09:25 +0000] [1] [INFO] Shutting down: Master

---I manualy requested a service url---

[2022-02-23 19:03:21 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2022-02-23 19:03:21 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[2022-02-23 19:03:21 +0000] [1] [INFO] Using worker: gthread
[2022-02-23 19:03:21 +0000] [3] [INFO] Booting worker with pid: 3
2022-02-23 19:03:21,985 - apscheduler.scheduler - INFO - Scheduler started

Honestly, I couldn't think of anything better than to keep track of instance restarts and request the URL every time.

I hope for your advice, thank you!


Solution

  • TBF, I'm not familiar with Google Cloud Run, but if I understand correctly the point is that you code will only be invoked when a request is made to the app, i.e. it's one of those "serverless" setups - is that correct?

    If so: What updater.start_polling() does is start a long running background thread that fetches updates continuously. To have your bot responsive 24/7 with this method, your script needs to run 24/7. Now the point of serverless setups is that your code only runs on demand, so for this hosting method a more reasonable approach would be to only invoke your code when your bot receives an update. This can achieved using a webhook instead of long polling. There is a section on this in the PTB wiki. See also this thread about AWS Lambda, which is similar AFAIK.

    However one should note that stateful logic like ConversationHandler is hard to realize in such setups: By default ConversationHandler keeps track of the current state in memory, so the information is lost on shutdown of the process. You can use persistence to store the data, but I'm not sure how well this works with serverless setups - there might be race conditions if multiple updates come in at the same time. So another idea would be to switch to a different hosting service that allows to run your process 24/7.


    Disclaimer: I'm currently the maintainer of python-telegram-bot.