I have a fastapi application that creates a websocket connection initiated by a react frontend. In it, I listen on a certain redis pubsub channel, and whenever a message is posted, I send that message to the front end with the same websocket object. This works as intended.
async def reader(channel: aioredis.client.PubSub, websocket: WebSocket): while True: try: async with async_timeout.timeout(1): message = await channel.get_message(ignore_subscribe_messages=True) if message is not None and message['type'] == 'message': await websocket.send_text(message['data'].decode('utf-8')) except asyncio.TimeoutError: pass await asyncio.sleep(0.01)@router.websocket("/api/ws/{id}")async def websocket_endpoint(websocket: WebSocket, station_id: str, redis: aioredis.Redis = Depends(get_redis)): await websocket.accept() channel_name = f'updates:{id}' try: pubsub = redis.pubsub() await pubsub.subscribe(channel_name) await reader(pubsub, websocket) except WebSocketDisconnect: print("WebSocket disconnected by the client.") except asyncio.CancelledError: print("WebSocket connection cancelled.") except Exception as e: print('WebSocket error:', e) finally: await pubsub.unsubscribe(channel_name) await pubsub.close() await websocket.close()
The messages posted to the pubsub are created in a celery task initiated in another api endpoint. This works as intended.
I run into problems whenever I try and exit the fastapi application (or if watchfiles detects a change and causes a reload), where it is unable to exit the async operations.
Here is the unicorn and error output:
WARNING: WatchFiles detected changes in 'server/routes/test_routes.py'. Reloading...INFO: Shutting downINFO: connection closedINFO: Waiting for background tasks to complete. (CTRL+C to force quit) # <--- FREEZES HERE^CINFO: Finished server process [3639091]ERROR: Traceback (most recent call last): File "XXXXX/env/lib/python3.10/site-packages/starlette/routing.py", line 747, in lifespan await receive() File "XXXXX/env/lib/python3.10/site-packages/uvicorn/lifespan/on.py", line 137, in receive return await self.receive_queue.get() File "/usr/lib/python3.10/asyncio/queues.py", line 159, in get await getterasyncio.exceptions.CancelledErrorWebSocket connection cancelled.INFO: Stopping reloader process [3638971]
I have tried redis-py and aioredis in different configurations, and some are unable to send to frontend but able to shutdown properly, and others are able to send through the socket but unable to shutdown properly.
I've looked into FastAPI's lifespan for allocating and deallocating redis resources but had troubles with it never reaching the shutdown point.
Nothing has quite stuck yet but any help on this problem would be greatly appreciated!