-
Notifications
You must be signed in to change notification settings - Fork 69
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
RuntimeError: Event loop is closed, when running through Flask[async] #1168
Comments
Could be the same as #1107. Seems like we would need a different connector for each connection, although I'm not sure why it works in the first place then (and why the connector even allows to be used without passing an explicit loop to use?). |
Hi @nioncode thanks for raising an issue on the Cloud SQL Python Connector! 😄 So first things first, this seems like you may have hit some edge case as regular async usage with Per our README's Async Driver Usage section and guidance you should either use Is there a reason you are not initializing the Connector with one of these approaches? I need to hammer down the different async usage patterns and make sure the Connector handles these gracefully. It seems there is definitely some room for improvements and paths that should error explicitly instead of failing after a period of time. Thanks for this data point. |
Thanks for the fast response! Honestly we just missed the Can you recommend a way to close the connector as soon as the underlying connection is closed in sqlalchemy? I had a look at sqlalchemy events, but it seems like all the connection close events are raised before the connection is actually closed, so it is probably too early to close the connector at this point. Also, I'm wondering if a workaround would be if the connector would forward its internal loop to the aiohttp ClientSession instead of letting the ClientSession default to the cururently running loop. Is there any downside in having the connector + aiohttp run with one loop and the remaining app with another? |
This I will have to look into, my first thought was close events as well, maybe the
The reason the aiohttp.ClientSession uses the running event loop is that |
I understand that this would be the optimal setup, but I'm afraid this just does not work with At the moment we are just using Why does the connector even support creating its own event loop, if it is not supposed to be used that way? |
Good question, the connector supports creating its own event loop because this is the way the sync drivers are intended to be used. However, for async driver (asyncpg) its expected that an event loop is already present. Looking back we should have created a separate |
FYI: I have fixed the issue with gevent in #1170 and will cut a release of the library next week to get it out the door. Will continue to look at this use-case as well. |
Thanks for the gevent fix! However, I think we are now in a weird situation where we cannot switch to gevent anymore, since our code base now uses async/await everywhere 😄 Can you give us a recommendation how we would best use the connector at this point through EDIT: we can't even migrate our entire app to quart at this point, since we have other dependencies that are not async-compatible (Google Cloud Storage, Cloud Tasks), so it would be best if we keep those in flask anyways. |
Can we maybe use a sync Connector and just hand it a pre-created event loop? Then everything related to the connector should happen on that loop and if we don't use a connection pool, then connections are not shared across multiple event loops and things should work mostly fine or? |
@nioncode couple clarifying questions for you.
Which database driver would you like to use? I am a bit confused by this sentence...
Yes you could definitely give this a try, creating a loop and initializing the sync Connector with it. However, this means you have to create multiple Connectors potentially for different connections if I understand your use case properly. Ideally the handling of event loops etc should be abstracted away from the user and inside the Connector logic. |
Initially (when we were running with regular flask) we used pg8000. We then switched to asynpg. As part of this issue, we now wanted to avoid going back to pg8000, but in the end this did not work out (see below).
Thanks. We played around with this a bit, but it was a real mess getting this to work properly with Long story short, we now moved back to using a sync engine (and connector) with pg8000 when running through I'm still wondering if it would be somehow possible to support Thank you for all your fast responses and help with this, it's a real pleasure to work with you! |
@nioncode thanks for the great clarification! I understand your dilemma well now. I plan to take some time soon to dive deep into a few ways in which I can make the async interface easier to use and will make sure to give In the meantime, I am glad the sync engine seems to be working. Thanks for all the details, makes my life much easier to reproduce this use-case. 😄 Will update this thread with any updates made. |
Will begin taking a closer look into async frameworks and their usage of Python Connector in the coming weeks... |
Bug Description
After seeing that gevent is not supported (#969), we started to port our app from flask to quart. At the moment, we successfully ported some endpoints to quart, but still have others that are kept in flask. To have a similar code structure, we now run flask with async views (using
flask[async]
as dependency) so that we can run the same async database driver etc. in flask and quart and don't have to maintain sync and async engines.After the app was idle for some time, we could not establish new connections to the database, since the event loop is closed (see stacktrace below). After restarting the running container, connections can successfully be established again.
From what I understood,
flask[async]
creates a new event loop for each request, i.e. the event loop of flask gets stopped after every request. That's why we use theNullPool
of sqlalchemy so that connections are not shared and new connections are established for each request (which is ok performance-wise for those endpoints that are still running in flask), since you cannot share connections across different event loops. This seems to work fine, since a new connection is created instead of re-using an existing one, so it is probably not the source of the issue.Further,
cloud-sql-python-connector
seems to spin up its own thread + event loop, which is used for internal processing, so this is probably not the issue either.What does NOT spin up its own event loop, is the
aiohttp.ClientSession
that gets lazily initialized duringconnector.connect_async
and seems to use the loop that is currently running whenever the first db connection gets established. What I think is happening here is that this event loop gets closed at some point and then theCientSession
can not perform any requests anymore. What I don't understand though is why the event loop gets closed (since it seems to not be the event loop started byflask[async]
for request processing, since that loop should be stopped after every request and so the next request should immediately fail).Should the
aiohttp.ClientSession
use the same event loop that the connector itself uses (which could just pass its internal loop to the session when initiating it) or is there a reason why it connects to the currently running loop?Example code (or command)
Stacktrace
Steps to reproduce?
Environment
python:3.12-slim
Docker containerAdditional Details
No response
The text was updated successfully, but these errors were encountered: