Can someone explain how it works under the hood. A Rust server for python obviously needs to still run the python interpreter. Is it running many instances to get around the GIL, and doing the request management / reverse proxy / runtime management in rust?
I'm confused, in the example application I see all these async endpoints interacting with the database, but the function bodies are entirely sync. Wouldn't this block the event loop?
Presumably, they would - hopefully, the work they're doing is quick enough that it doesn't really matter that they're blocking (this is usually true when interacting with sqlite, for example). Async request handlers are nice to have for when you do want to do something genuinely async (like firing off another HTTP request to somewhere else)
I can't imagine someone having the chops to code up their own web framework and making a mistake like this. There must be something that we're missing. Another commenter suggested that since it's SQLite, the blocking calls are quick enough not to care, which may be true. I don't know enough about SQLite's performance characteristics to judge that.
In general I assume disk I/O is slow enough (even on SSD) to care about blocking the event loop and delegating the calls to a thread. But I've been wrong about this before. For example, a call to stat is technically blocking, but the overhead of delegating it to a thread is actually more expensive (on my Framework 13) and blocks the loop longer than just doing the call outright in the main thread.
To GP: yes, it would block the event loop and that's pretty bad. But...
asyncio.to_thread[0] as a quick workaround, just keep track of your thread count. Obviously frameworks and libraries with asyncio support should be chosen whenever possible.
> Overall there are also places where functions are async running in sync bodies, but they're not running on main thread.
That would be a design mistake, as functions have colors[1] with async/await. That's currently my biggest gripe in the python ecosystem, apart from modules maintaining their own hidden state.
Especially over Flask and Django, Robyn additionally offers integrated websocket support along with many existing features like templating, logging etc.[1]
Developer has been working on this project for a few years now, here is the updated roadmap, shared by the developer and maintainer of the project.[2]
Can't speak for Robyn author, but in my opinion it's not a valid comparison. The only thing Granian and Robyn have in common is the usage of Rust and PyO3, but that would be like comparing two projects using Cython.
Granian is just a server, and as such it is comparable to projects like uvicorn, Daphne, hypercorn, gunicorn, uwsgi. It can be used to run any WSGI or ASGI application out there.
Robyn is a web framework, and thus it does a lot more than Granian, and as such it can be compared to other async first web frameworks, like starlette, litestar, sanic, quart, Emmett, blacksheep.
If we're talking about performance then.. well it's still Python running the app code. There's no way to escape that.
If we're talking about features, ergonomics, etc. then again, Granian is just a server, so there's not so much to talk about :)
Based on the [1] one of the obvious places where Robyn comes at top is in terms of latency of Websockets and OpenAPI Docs generation. I've tagged the author for a detailed response.
golang has frameworks like gin that are faster and simpler when building barebones apps, the more features you need the more modules increase. Robyn brings all the features out of the box, I know it's a compromise but a good one over eventual complexity of the application in the long term.
Why do all those micro framework copy the worst aspects of flask?
The global request object, the bad integration with sqlalchemy, the strong coupling with an application object that forced the indirection of blueprints...
If you start again from scratch, forcing people to migrate their stack and therefor leave their ecosystem behind, at least fix the glaring API problems.
If you hate Python so much you need to replace the linters, package managers, and now Web frameworks with Rust, why not just go all the way and write everything in Rust?
Python's success is partly because it is easy to do things. It has a huge market share. Combining the speed advantage of one programming language, with another seems like a great idea.
Is it really going to be faster? In the benchmark they cited in the README [0], they are in 10th place among Python frameworks. This is not a real-life benchmark, as it merely expects a hardcoded response. In fact, Robyn’s 10th place is thanks to a feature designed for benchmarks like this, i.e. marking responses as constant [1]. Three more frameworks beat Robyn if you disable this benchmark optimization.
Can't speak for Robyn since that is competing in an already aggressively competitive landscape (webserving). Whereas introduction of RUFF/UV by astral has been exceptional.
The problem with all this combining is that it's making apps less portable. Like I want to make apps that work on desktop+mobile with flet[0], but now I have to specifically seek out more "traditional" alternate packages and hope they stayed in reasonable feature parity with these Rustified Frankensteins. Not a fan at all.
Correct me if I'm wrong, but the way you use "traditional", basically means "written in C", right? I certainly can sympathize with your point about the frustration of compatibility issues in your narrow usecase (though I don't agree that it should affect what options we have), but connecting to the original commenter's point, it's really common for libraries in python to be wrappers for things written in other languages.
The use case isn't that narrow though. Just the flet project has 12.5k GH stars (which I would say represents significant adoption), and I'll wager there are other projects out there running in random environments that have Python support but bringing in Rust will be a serious pain. It's degrading the ecosystem IMO. The only other language I'm aware of that has significant library wrapping is Fortran, and those libraries have on par or better alternatives in C as well as are now essentially legacy.
There’s native modules to speed up hot paths, and there’s writing 29% [0] of your framework in Rust, entirely sidestepping interoperability standards, like ASGI.
[0] https://github.com/sparckles/Robyn says 50% Python, 25.3% JS, 20.7% Rust — but all the JS code seems to be for their docs site, so it is reasonable to say 20.7 / 70.7 = 29.2% is in Rust.
Careful now. That kind of provocative black-and-white claim will only alienate the 50.1% of American voters who appreciate Mr Joker’s anti-government and anti-woke agenda.
The biggest differentiator between Robyn and frameworks like FastAPI or Sanic seems to be the Rust runtime. But is Python's async actually the bottleneck that Rust is solving here?
Actual bottleneck in examples is sqlalchemy, like 10x overhead easily. I find it quite funny. There is no real difference in web frameworks for real apps in terms of overhead.
I hear this sort of thinking repeated often, and it makes sense at an intuitive level.
However, my experience is that this isn’t always the case. My company has a medium-large Python web app in production, and having viewed performance profiles, I can say definitively that Python code execution occupies a non-trivial amount of processing time. Of course the single largest time sink is the LLM requests we make, but if we could magically nullify the Python processing time, our latency would significantly improve.
The culprit in this case may be specific to FastAPI and Pydantic rather than Python generally, but the point still holds that not all web frameworks and resultant apps are equivalent.
That said, I’ve been dreaming of rewriting large chunks of it in golang. I’ve always known Python was relatively slower than other languages, but I saw a recent benchmark measuring golang as 50x faster than Python. Perhaps the benchmark won’t translate to real world performance, but still - 50x is difficult to ignore.
Returning static "hello world" response for slowest python framework is easily not bigger than 1ms (for fast frameworks it's 0.1ms or lower). Good luck to make the rest of your app to make something comparably fast if db or network is involved.