11import asyncio
2+ import datetime
23import logging
34import threading
45from typing import Final
2122_logger = logging .getLogger (__name__ )
2223
2324_LIFESPAN_TIMEOUT : Final [int ] = 10
25+ _FASTAPI_STARTUP_TIMEOUT : Final [float ] = datetime .timedelta (minutes = 1 ).total_seconds ()
2426
2527
2628def on_worker_init (sender , ** _kwargs ) -> None :
27- def _init_fastapi () -> None :
29+ startup_complete_event = threading .Event ()
30+
31+ def _init_fastapi (startup_complete_event : threading .Event ) -> None :
2832 loop = asyncio .new_event_loop ()
2933 asyncio .set_event_loop (loop )
3034 shutdown_event = asyncio .Event ()
3135
3236 fastapi_app = create_app (ApplicationSettings .create_from_envs ())
3337
34- async def lifespan () :
38+ async def lifespan (startup_complete_event : threading . Event ) -> None :
3539 async with LifespanManager (
3640 fastapi_app ,
3741 startup_timeout = _LIFESPAN_TIMEOUT ,
3842 shutdown_timeout = _LIFESPAN_TIMEOUT ,
3943 ):
4044 try :
45+ _logger .info ("fastapi APP started!" )
46+ startup_complete_event .set ()
4147 await shutdown_event .wait ()
4248 except asyncio .CancelledError :
4349 _logger .warning ("Lifespan task cancelled" )
4450
45- lifespan_task = loop .create_task (lifespan ())
51+ lifespan_task = loop .create_task (lifespan (startup_complete_event ))
4652 fastapi_app .state .lifespan_task = lifespan_task
4753 fastapi_app .state .shutdown_event = shutdown_event
4854 set_event_loop (fastapi_app , loop )
@@ -52,8 +58,15 @@ async def lifespan():
5258
5359 loop .run_forever ()
5460
55- thread = threading .Thread (target = _init_fastapi , daemon = True )
61+ thread = threading .Thread (
62+ target = _init_fastapi ,
63+ name = "fastapi_app" ,
64+ args = (startup_complete_event ,),
65+ daemon = True ,
66+ )
5667 thread .start ()
68+ # ensure the fastapi app is ready before going on
69+ startup_complete_event .wait (_FASTAPI_STARTUP_TIMEOUT )
5770
5871
5972def on_worker_shutdown (sender , ** _kwargs ):
0 commit comments