http://techspot.zzzeek.org/2015/02/15/asynchronous-python-an...
The alternative being just using SA Core and something like aiopg, using asyncpg, or using SA in a threadpool (unfortunately, a technique that’s involved enough that the recipe for using SA isn’t a 5 min “gotcha”, re: flask).
I like SA. I’ve used it for years, and you’ve helped me on the forums, so I’m appreciative for the work you’ve done. I just don’t understand why you’ve written off an entire style of programming.
It's not that the ORM is less "competitive" because I am certainly able to make an async version of SQLAlchemy, it's just I don't have the time / interest / resources to do so, and as I've gone through the effort to write about in detail, it is a bad idea. If you want to pay me a salary to make an async SQLAlchemy I can totally do that. It would be a worthwhile effort in that it would make people happy. But also it would be a wasteful effort in terms of advancing the field of Python database programming. That's the decision I'm facing and it would be a lot easier if folks would just realize asyncio isn't getting them much overall.
It's like, suppose I make bikes. But everyone wants to get to work on pogo sticks instead. Everyone who uses pogo sticks is terminally late to work, they're getting injured all the time, they are miserable, but someone told them "hey pogo sticks are FASTER than bikes!", people were just bored with bikes so much that they didn't even bother testing this assertion, and now everyone just has discussions like "oh well I have this shiny new pogo stick, so I only broke three ribs this week! hooray!" and people have just forgotten that there is nothing wrong with bikes at all and they are in fact a lot better than pogo sticks for the task of getting to work every day (you might find pogo sticks to be more interesting and fun in the moment, but as a real solution to the problem, they are not any better than bikes and probably worse). Someone who makes bikes tries to point all of this out. That puts you in the position of saying, "all your arguments about bikes being better than pogo sticks for getting to work, who cares. you are just concerned pogo sticks are a threat to your bike sales". Well, yes, they are. But the bigger issue is that it is ridiculous everyone is killing themselves trying to get to work on pogo sticks. Somehow I feel that should not be lost.
> I just don’t understand why you’ve written off an entire style of programming.
I've written and tweeted about this a lot so feel free to point out the reasons that might not be clear. To recap http://techspot.zzzeek.org/2015/02/15/asynchronous-python-an...:
asyncio is essentailly intended to provide an interface around the concept of non-blocking IO. The relational database use case, for the vast majority of uses (e.g. CRUD), gains nothing from using non-blocking IO (most databases don't even offer a non-blocking API anyway) and only complicates the application, introduces subtle bugs, and does not improve performance. The other argument made about asyncio is that replacing the OS'es task scheduler with a cooperative one defined by when IO happens to occur is "safer", because context switches are no longer implicit. The second section of the post above makes many arguments why this is not the case and especially not for modern server-side application design which is inherently multi-process.
Relationships as properties for example, I just don’t know how you’d solve attribute access in an idiomatic way (sure it could return a future, so maybe the idea is that you’d get a future subclass that still supports operations)?
Anyway, the problem is that it’s really difficult to mix sync/async code. And as soon as you’ve got a need for websockets, you’re pretty much thrown into the world of asyncio. To use the ORM is then a threadpool issue.
Anyway, my approach has instead been to use SA Core (with aiopg). I’ve probably always gotten to involved with my Mappers anyway.
I think the answer would be that you just ditch "lazy loading" altogether, in the spirit of asyncio's theme that "implicit IO is bad", you'd have to have explicit conversations with the DB. Loading attributes from relationships is one thing, but there's also the notion that other kinds of attributes can refresh themselves if they happen to have been expired, which is something the ORM does when a server-side rule or a transaction boundary that might mean the attribute changes on the database side. It's a very elegant and simple solution to the problem but if you have decided that implicit IO is bad, it means you don't really want that anymore. You'd just start treating an ORM mapped object more like an active row and you'd have yields on most operations, and you'd also want to add more coarse-grained boundaries to when state is expired and re-loaded (e.g., instead of expiring particular attributes as needed to be reloaded when accessed, there would need to be a more formal "refresh the object" kind of step).
That's one way to do it. Another way would be if an implicit non-blocking approach such as gevent could be cleanly integrated with asyncio - then object heavy database interactions could simply be inside of a block written in "blocking IO" style, and that entire block is entered as a yield. There is no technical reason this can't be done. The reason is cultural; people who like asyncio really hate implicit IO. So trying to make that work seems like it wouldn't be very worth it either.
> Anyway, the problem is that it’s really difficult to mix sync/async code.
I think this is because people just aren't working on the problem. You read asyncio tutorials and they say things like, "if you really MUST use evil threads...here's this crappy executor thing we think you should never use", I'm exaggerating a bit but that's kind of the vibe they send off. Making the asyncio / thread transition could be done very nicely, and there can also be integrations between gevent style and asyncio style, and finally there are many other ways to use non-blocking IO without using a full event style programming interface. The asyncio crowd isn't there yet. They are still having the endorphin high of asyncio "clicking" for them and they are rigidly dogmatic and pretty short on real benchmarks that show actual gains. I have nothing against using non-blocking IO but the next time I have to use it, I'm going to purposely build a non-blocking IO library that seamelessly makes use of threads and queues and shows how easy it is if you really need to reach out to a dozen slow web services at once to get the non-blocking IO advantages without turning your entire codebase and dependencies inside out.
> Anyway, my approach has instead been to use SA Core (with aiopg)
right, so another thing to keep in mind, Postgresql is the only database that has a native non-blocking protocol. So building an async ORM is immediately limited by that - for every other database there would still be a threadpool running the socket conversation, and having every database message be a separate threaded unit is less efficient than just having the entire transactional conversation in one threaded unit (hundreds or thousands of shifts in and out of the thread pool vs. just one). There's really many reasons to not write an asyncio ORM right now.
Async(io) is not a "style of programming"; it is an approach to solving a certain class of problems which arise undercertain conditions, and is only really useful if it fits your use case. I'm certainly happy that Python has introduced it on a low level, but I definitely won't use it in situations that don't warrant it -- the same holds true for many other Python idioms, like say ABCs or Ellipsis.
Yeah, I see it as an alternative concurrency model to threads. And I think each has its pros.
As for you not using it in situations that don’t warrant concurrency, that’s a good idea.
At first though, learning async programming tastes like eating a garden snail.
Eventually however the bell in your head rings and it all becomes second nature.
At that point you have the ability to do a bunch of things in parallel in an intuitive and robust way.
Also async is good for solving certain categories of problem that are harder in synch, like starting a sub process and reading and immediately responding to stderr, whislt also doing other things, without the issues that comes with multithreading.
What async programming really gives you is event driven programming - the ability just to set up functions to run when certain things happen, as opposed to traditional ... do this step one then do this step two until program finishes. Again quite foreign if you havent done much event driven programming but it’s really powerful.
It is true that certain libraries need to be rewritten to live well in the async world, but that’s ok I think ... it’s not like the 2/3 issue.... you don’t have to go there if you don’t want to.
The other thing that is not immediately obvious when learning async is that once you know what you’re doing, in many cases sync and async style can be naturally interspersed, and while that might sound like a recipe for complexity and pain, in fact it isn’t, because as mentioned earlier, async actually isn’t that hard or complex once you grasp the mental model, and interspersing both just works pretty well. You won’t know how to do that in a simple manner though till you’ve been through the grind of making it hard for yourself because at first you’ll assume that async is much harder than it really is and you’ll be looking for complexity and indeed coding complexity.
I certainly understand the initial distaste that people might have for async because it’s so foreign. It’s made harder in Python because the approach has changed so there’s multiple ways to do the same thing, like “yield from” and “await” which are effectively the same thing. Different ways to do the same thing in a conceptually new and hard idea space makes the hard thing about learning async programming even harder.
BUT, like most things with programming, once you’ve learned it, what at first looked really hard is in fact fairly straightforward, with just a few different rules to follow and that’s pretty much it.
My advice to any python programmer is to invest the time and practice a lot and work hard to master async. It’s not going away, and it will constantly annoy you that this async thing is everywhere but you don’t know it well so you’ll feel excluded and want to disparage it. Get past that and turn it into one of your strengths and a power tool.
It’ll just taste like garden snails while you’re still in that alien learning zone.
Remember that it takes an expert programmer with superior knowledge to know how to do things in the simplest way.
you actually don't need to use non-blocking sockets in order to write callback-oriented code. Nor do you need event-driven programming to use non-blocking sockets. this whole argument is about syntactic sugar but below the surface it is also about confusion over these two distinct concepts.
I hope one day he’ll become a real fan of async as it would be good to have all that skill directed into the async space, which frankly needs him.