a year ago
Hi all,
Just want to share a problem I had, and the solution, in case it's helpful to others and may come up in Google results.
I have an app written with Falcon, which is a lightweight Python framework, but it's harder to find resources for than others like FastAPI. It's being served via uvicorn, and I'm using ASGI for server-sent events (SSE). My app wraps AI, so the response to a request creates a request, which is itself streaming / async, and then sets the Falcon response.sse to a generator that handles the incoming SSE events and forwards them. All this is done via Python async / coroutine methods. Currently, the request made to fulfil a response is not made manually but via the OpenAI Python API, and I do not know what libraries it's implemented with. The code just reads its results from the async streaming API: https://github.com/openai/openai-python?tab=readme-ov-file#streaming-responses, ie, the yielded values there are in turn yielded to Falcon's SSE response.
Not many people seem to be using SSE with Railway and all I've seen is this page for checking SSE status: https://sse.up.railway.app
My app has a /hello endpoint, which simply streams text split up as words and is a good test SSE is working at all (and it was) and other endpoints with the more complex setup of making a request and wrapping it described above.
The /hello endpoint worked for SSE.
All other endpoints returned the entire response in one go, not streamed. Yet logging showed that the responses were being received, and generated in return, correctly over time. These endpoints also all worked locally on my test machine with uvicorn.
The cause turned out to be the uvicorn event loop.
Hints came from:
This Falcon bug report, where vytas7, the Falcon lead, said "streaming both the request and response at the same time, maybe some complications arise from that". That bug was closed, but it was a good hint.
Knowing Python coroutines have an asyncio event loop
The Uvicorn docs: https://www.uvicorn.org where there are multiple mentions of which event loop is used, and a hint that it may be using its own uvloop event loop as the default 'auto' value instead of standard asyncio
The solution: start Uvicorn using the asyncio event loop.
My Railway custom start command is:
uvicorn myapp:app --reload --host 0.0.0.0 --port $PORT --loop asyncio
where the key is that last part, --loop asyncio.
Hope this helps others in future!
2 Replies
a year ago
very helpful, only one issue, the sse example link you sent doesn't work, but this one does https://utilities.up.railway.app/sse
a year ago
You are right! I must have found the wrong one in my history when writing the post, thanks for noticing. Unfortunately I can't edit the post…
I tested via:
curl --verbose -H "Accept: text/event-stream" -N https://sse.up.railway.app/sse