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
🐛 Allow exit code for dependencies with yield
to always execute, by removing capacity limiter for them, to e.g. allow closing DB connections without deadlocks
#5122
Conversation
Codecov Report
@@ Coverage Diff @@
## master #5122 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 532 539 +7
Lines 13684 13906 +222
==========================================
+ Hits 13684 13906 +222
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
📝 Docs preview for commit c507e76 at: https://62cb8e56fb08263a0b2a8795--fastapi.netlify.app |
If a new The inf would make sense for a global limiter... Isn't it the case? |
Yeah I guess it can be 1. Or we can make it a global. Either way. |
📝 Docs preview for commit 0892b02 at: https://62cbaec8c51dc34d83b10e22--fastapi.netlify.app |
📝 Docs preview for commit d822171 at: https://62ccd4afa2dc031bf1f8e7af--fastapi.netlify.app |
📝 Docs preview for commit 3557911 at: https://62cf1d1ed35ee72b45e8b61f--fastapi.netlify.app |
📝 Docs preview for commit c53e0f6 at: https://62cf1e6ca2b9a32bbabee427--fastapi.netlify.app |
📝 Docs preview for commit 3b73873 at: https://62d43b8f94f05f7127e81a5e--fastapi.netlify.app |
Niceee! 🤓 🚀 I tested this with the example code in: #3205 I used $ wrk -c 100 -t 100 --timeout 60 "http://127.0.0.1:8000/" Without the fixWithout the fix, I got first:
And then the second run I got:
...this seems to reflect the deadlock. 🎉 (I'm celebrating being able to see something broken, it's sometimes hard to see the broken part 😅 ) After that, I
After 7 minutes I force-killed the process. So, indeed, deadlock. With the fix, outsideWith the fix, putting a single global First I got:
For the second run I got:
Third run:
Not sure why it keeps reducing the number of requests. It seems something would be still wrong, but not sure what. 😔 After that, and after I wrote this, I I ran the whole thing again, and I
But after 50 seconds it terminated normally. With the fix, localKeeping the local The first run I got:
The second run I got:
And the third run I got:
The results are similar to having a global Terminating the process also worked as expected instead of hanging there in a deadlock. For some reason, it's still being able to handle fewer requests every time. Maybe it's just that it's trying to read the file every time or something like that and there's a bottleneck in that, just in the file system. I'm not sure about that. Checking the AnyIO docs I see that:
Ref: https://anyio.readthedocs.io/en/stable/synchronization.html Then I'm not sure if it could be problematic if the same thread worker tried to get the limiter again, and if that could block with the other thing running on the same thread that already got the limiter token. Until we figure this out, I'll leave the local But anyway, it seems that this clearly improves the situation. I wish there was a way to test this, in pytest. But not sure how to replicate the whole problem and situation in a feasible way (not involving a whole setup for "benchmarks" with For now, I'll merge this, but if anyone has any idea about how this could be tested, to have checks in CI making sure the problem doesn't happen again, it would be awesome. Thanks @adriangb for all the investigation and work! 🙇 🤓 🍰 |
yield
to always execute, by removing capacity limiter for them, to e.g. allow closing DB connections without deadlocks
It might be reproducible by talking pure-ASGI to the FastAPI app and launching multiple tasks using a TaskGroup? Might be hard to get the timing right though. You could try setting the capacity limit to float("inf") instead of 1. I think the slowdowns might come from acquiring database connections. There's likely some semaphore there as well. |
Hehe I was writing this and you commented before me 😅 Ah, wait, I didn't read @Kludex comment properly. I didn't realize it could be an inf float. That probably makes sense and we can avoid creating a new object every time. I just di that and updated the branch. 🚀 And yeah, maybe multiple ASGIs in a task group could work. But I'm not sure how to make it fail instead of hang, if it just hangs it's gonna be more difficult to test. 🤔 |
Hehehe the tests have spoken... it seems we need to use the local variable after all. Otherwise AnyIO gets confused with what is the async backend. 😂 |
…nc context to create the object This reverts commit 09dc61a.
📝 Docs preview for commit 09dc61a at: https://6314efcd67c58a208eaa7d3e--fastapi.netlify.app |
wait |
I think anyio.fail_on_after() should do the trick right? But if we were to put this in a test it might be good to figure out how to reproduce it without SQLAlchemy. |
📝 Docs preview for commit c7e3263 at: https://6314f0dd00ed39272fd435c8--fastapi.netlify.app |
https://anyio.readthedocs.io/en/stable/api.html?highlight=runvar#anyio.lowlevel.RunVar |
Totally agree with that! That would be great. Not sure how but it would be great indeed. Thanks for the links and pointers! (also on Discord) Copying here for completeness, the issue about The snippet to update the default token size of the default In conclusion, for now I'll merge this with the local capacity limiter. Maybe at some point Starlette or FastAPI can have a way to customize different capacity limiters for different things (not sure, I haven't thought much about it) and then this could be set there. And about the option of using a Thanks again you both for all the help! 🙇 🍰 |
This has been closed, but I am not really sure on the final solution as far as running syncronous SQLAlchemy with FastAPI. In my case, I am using FastAPI with SQLAlchemy and Oracle, which does not have a trusted async library. |
@nickswiss I'm not sure I understand the question or if there's something you're not being able to do. If you still have questions or problems, it's probably better to ask in a new GitHub issue, the template will guide you to structure the question in a way that will make it easier to help you, with an example I can run, etc. |
Related to #3205
Tested this against https://github.com/harsh8398/fastapi-dependency-issue