Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite task using loop.create_task in main_process_start is hanging. #2782

Closed
1 task done
lxdlam opened this issue Jul 10, 2023 · 6 comments
Closed
1 task done

Infinite task using loop.create_task in main_process_start is hanging. #2782

lxdlam opened this issue Jul 10, 2023 · 6 comments

Comments

@lxdlam
Copy link

lxdlam commented Jul 10, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I've using Sanic with discord.py to start both a discord bot and a Sanic server. I'm initializing the discord bot in a function like:

@app.main_process_start
async def start(app: Sanic, loop: AbstractEventLoop) -> None:
    loop.create_task(bot.start(CLIENT_TOKEN))

The bot.start call is a async function which is expected to enter a infinite loop. When I start my server through sanic main:app, the discord server will never go live, and after sending Ctrl-C, it seems the coroutine is stucked at making connection and prints a error message Fatal error on transport TCPTransport.

If I change the decorator into @app.before_server_start, it works with no issue, but I want to make only one global instance of the client, instead of making one each worker. Also, I think the behaviour is weird since I have digged into the code and seen nothing special.

Code snippet

No response

Expected Behavior

No response

How do you run Sanic?

Sanic CLI

Operating System

Fedora 38 - Linux 6.3

Sanic Version

v23.3.0

Additional context

No response

@lxdlam lxdlam added the bug label Jul 10, 2023
@sjsadowski sjsadowski removed the bug label Jul 10, 2023
@sjsadowski
Copy link
Contributor

Can you please share the link to your discord bot's code so we can understand what it's trying to do on startup?

@lxdlam
Copy link
Author

lxdlam commented Jul 10, 2023

Can you please share the link to your discord bot's code so we can understand what it's trying to do on startup?

Sure. It's the minimal snippet:

  • bot.py
from discord.ext import commands # discord.py, https://discordpy.readthedocs.io/en/stable/

class Bot(commands.Bot):
    def __init__(self):
        # ...

    async def on_ready(self):
        logger.info(f"Discord bot connected. Logged in as {self.user} ({self.user.id})")


bot = Bot()
  • routes.py
import sanic  # for typing stuff
from sanic import HTTPResponse

app = sanic.Sanic("sanic_server")


@app.route("/")
async def index(_request: sanic.Request) -> HTTPResponse:
    return HTTPResponse("<p>Hello World!</p>")
  • main.py
from os import environ

from sanic import Sanic
from asyncio import AbstractEventLoop
import routes
from bot import bot

CLIENT_TOKEN = environ.get("DISCORD_TOKEN")

app = Sanic.get_app("sanic_server")


@app.main_process_start
async def start(app: Sanic, loop: AbstractEventLoop) -> None:
    loop.create_task(bot.start(CLIENT_TOKEN))

@Tronic
Copy link
Member

Tronic commented Jul 10, 2023

I believe the main process doesn't run in async mode. This test case shows that the task is only executed for one round (until the await), thus this prints "Running..." only once. If used in server process, it prints the message each second.

import asyncio
from sanic import Sanic

app = Sanic("sanic_server")

async def worker():
    while True:
        print("Running...")
        await asyncio.sleep(1)

@app.main_process_start
async def start(app, loop) -> None:
    loop.create_task(worker())

@app.route("/")
async def test(request):
    ...

As a workaround, spawn a process from main_process_ready and join it in main_process_stop. If you need await, use asyncio.run within your process for your very own event loop. The main process is not quite designed to run extra tasks by itself.

@lxdlam
Copy link
Author

lxdlam commented Jul 11, 2023

I see the reason and the workaround is acceptable. Thanks.

@lxdlam lxdlam closed this as completed Jul 11, 2023
@Tronic
Copy link
Member

Tronic commented Jul 11, 2023

See also: #2775

@ahopkins
Copy link
Member

I would not suggest running it in the main process. You should probably run it in its own process.

https://sanic.dev/en/guide/deployment/manager.html#running-custom-processes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants