ants-azure-demos icon indicating copy to clipboard operation
ants-azure-demos copied to clipboard

Startup and shutdown events not triggered

Open MrToustous opened this issue 3 years ago • 5 comments

Hi, and first of all thank you for your work ! I'm currently working on a webapp hosted on Azure and wanted to go serverless; your AsgiMiddleware was a huge help. However I'm trying to use with with a FastAPI app where I instantiate a MongoClient at the startup of the app (much like here : https://github.com/markqiu/fastapi-mongodb-realworld-example-app/tree/master/app). When I publish my function and test it, the Mongo client doesnt seem to be set. Maybe because the startup events of the app are never triggered ? Do you have any idea on how I could make this work ? Thanks !

MrToustous avatar Jan 12 '22 15:01 MrToustous

Hmm, I don't know if that would be possible since in the serverless/functions model there isn't really a startup event and the function state isn't persistent. Even if there were a workaround, it'll likely be really slow.

Your best bet I think would be to put this in a container and run the container to Web Apps

Here's an example GitHub Actions script https://github.com/tonybaloney/try-pyjion/blob/main/.github/workflows/azure-container-webapp.yml

Please let me know if this works for you

tonybaloney avatar Jan 13 '22 03:01 tonybaloney

Thanks for your answer ! I'm currently running the app in a container in a WebApp and it works just fine. However I wanted to leverage the scaling capabilities of serverless functions. The fact that the model isnt persistent got me doubting it was a viable solution at first, but Azure best practices recommends reusing static clients in functions invocations (https://docs.microsoft.com/en-us/azure/azure-functions/manage-connections?tabs=csharp). This seems to indicate that I could startup the app once and persist it across functions calls. I found this useful post : https://github.com/Azure/azure-functions-python-worker/issues/911. I had to fix some asyncio loops shenanigans with nest_asyncio for everything to run on Azure functions. I does feel "workaround-y" but the following code seems to be working. Any thoughts ?

import azure.functions as func
from app.main import app

from azure.functions._http_asgi import AsgiResponse, AsgiRequest
import nest_asyncio

IS_INITED = False

nest_asyncio.apply()


async def run_setup(app):
    """Workaround to run Starlette startup events on Azure Function Workers."""
    global IS_INITED
    if not IS_INITED:
        await app.router.startup()
        IS_INITED = True


async def handle_asgi_request(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    asgi_request = AsgiRequest(req, context)
    scope = asgi_request.to_asgi_http_scope()
    asgi_response = await AsgiResponse.from_app(app, scope, req.get_body())
    return asgi_response.to_func_response()


async def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    await run_setup(app)
    return await handle_asgi_request(req, context)

MrToustous avatar Jan 13 '22 14:01 MrToustous

@MrToustous and @tonybaloney I also faced the same issue @tonybaloney Your Above Answer about startup events in serverless helped me to find the problem Thanks a lot for that

Here's How I Fixed This Issue But I am not sure about the drawback of this method because I don't have much knowledge about the async world in Python, I would love to know in which cases this code can break

connection_string = os.environ["DCS"]
logging.info(f"connection string {connection_string}")
async def init():
    client = AsyncIOMotorClient(connection_string)
    await init_beanie(database=client['Tradex'],document_models=[Admin,User])

loop = asyncio.get_running_loop()
asyncio.set_event_loop(loop)
loop.create_task(init())

kalilinux-png avatar Aug 06 '23 13:08 kalilinux-png

I've got a PR to the Azure Functions library to add support for startup and shutdown events in ASGI applications. If you need this feature ASAP, you could install the library directly from the branch by modifying requirements.txt and pointing to the branch URL, then using this environment variable

https://github.com/Azure/azure-functions-python-library/pull/187

tonybaloney avatar Aug 07 '23 02:08 tonybaloney

@tonybaloney I was unaware of this PR Thanks a lot 👍🏻

kalilinux-png avatar Aug 07 '23 02:08 kalilinux-png