Live data from Hacker News

Asynchronous Python and Databases

techspot.zzzeek.org

51–60 of 76 posts

Re: Asynchronous Python and Databases

#51
Async has never been about speed. Cooperative multitasking doesn't magically gzip your instruction pipeline or something (actually CM is suboptimal). It's just the only tool for those of us who want to avoid giving the keys to arguably the most important subsystem in the kernel to agents outside our control: The scheduler.

Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed to log in, it'll be very very difficult to get htop to execute when it's competing with 2k other processes for cpu time.

So, spawning a thread for every incoming connection request is a bad idea. But what about spawning a thread for every outgoing database connection?

The memory overhead of a thread compared to a tcp socket may be significant, but it's nothing compared to a database connection and all the other resources tied up serve that one database query. And best of all, you can refuse to setup a database connection without upsetting your user and e.g. serve from cache instead, but you can't refuse an incoming connection request.

According to the Postgres guys [1]: "A formula which has held up pretty well across a lot of benchmarks for years is that for optimal throughput the number of active connections should be somewhere near ((core_count * 2) + effective_spindle_count)"

Assuming you got two tablespaces on two raid 1 arrays on a 16 core machine, that's 16 * 2 + 2 = 34 threads plus the event loop thread, maximum.

According to those numbers, there doesn't seem to be much stopping you from running your data access logic within a thread.

[1]: https://wiki.postgresql.org/wiki/Number_Of_Database_Connecti...

Re: Asynchronous Python and Databases

#52
post #20

Great post. At this point those who blindly advocate async programming as generally faster just show their level of proficiency (a lack thereof). The fact that threads can be just as performant (or as we saw, even more performant) for IO code should not be surprising for anyone who knows how stuff works at the lower levels. BTW this irrational "async is always webscale" crap has been happening in the Java community a…

> With Twisted I had to go find for Twisted versions of drivers for databases. Now for asyncio I would have to look for asyncio version of libraries.

Isn't this an argument in favour of asyncio (in as much as it is/becomes the "one blessed/stlib interface")? Migration will take time, of course. But that is the nature of change...

Re: Asynchronous Python and Databases

#53
post #51

Async has never been about speed. Cooperative multitasking doesn't magically gzip your instruction pipeline or something (actually CM is suboptimal). It's just the only tool for those of us who want to avoid giving the keys to arguably the most important subsystem in the kernel to agents outside our control: The scheduler. Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed…

"Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed to log in, it'll be very very difficult to get htop to execute when it's competing with 2k other processes for cpu time."

Nothing about your comment is compatible with my experience. It is certainly not true that a Linux box with 2000 threads blocked on i/o will be having any sort of bad time. If you're really got 2000 threads competing for CPU time then your problem transcends execution architecture: you've simply admitted more work than you can reasonably discharge. Neither threads nor callbacks can solve this problem for you.

Also I'm not sure why you think that the kernel thread scheduler is all that good, or why it shouldn't be considered "outside our control." For my users the kernel thread scheduler is just another black object inside a dark box. The standard scheduler is pretty good for general purposes but I doubt its optimal for any particular case. In some loads it might be useful to yield to a specific thread that you think is holding a mutex your thread needs to acquire. This is cooperative multitasking, basically.

Re: Asynchronous Python and Databases

#54
Zeek,

I've been lucky enough to have benefitted from Sqlalchemy and Mako (but it's been awhile). Thanks.

This article looked like it was going to hit the sweet spot of stuff I'm curious about, but I found I was still left with questions. If you (or anyone) will indulge me... I'll try and ask a question to help clarify matters.

I work at a University on legacy ERP system(s). During registration there are 800+ concurrent connections but normally it floats around 200. Most all of these connections are idle. As you pointed out, a db may not be io/context bound (still hazy on that one). At the end of the day I consider myself technically astute, but basically a crud business programmer. I understand ACID and transactions; threads and async, maybe not so much.

Where I've always thought async could provide benefit would be in the following scenario. Our apps make a large # of procedural db calls today. If after studying them I realize that many are independent (i.e. reads) and could be 'batched' could that not provide a big performance/latency improvement? I.e. instead of the serial sequence of calls that happen now (even if a stored proc), async allows me to submit multiple sql calls. What I'm calling batch. In this ideal world, sqlalchemy would take care of the details (perhaps with some guidance directives as to whether ordering of results was important) and assemble the results.

Is this not a possible future 'async' sqlalchemy with superior responsiveness? Don't threads block on each sql request?

Re: Asynchronous Python and Databases

#55
post #54

Zeek, I've been lucky enough to have benefitted from Sqlalchemy and Mako (but it's been awhile). Thanks. This article looked like it was going to hit the sweet spot of stuff I'm curious about, but I found I was still left with questions. If you (or anyone) will indulge me... I'll try and ask a question to help clarify matters. I work at a University on legacy ERP system(s). During registration there are 800+ concurre…

Ultimately, those queries you want to batch are either:

1. Not critical to the logic of your app and thus should be fairly obvious candidate for caching.

2. Critical to the logic of your app and quite possibly done within a specific DB connection / transaction for locking reason.

Trying to be too clever and parallel often flounders on mundane things like running out of DB connections.

Re: Asynchronous Python and Databases

#56
post #52
post #20

Great post. At this point those who blindly advocate async programming as generally faster just show their level of proficiency (a lack thereof). The fact that threads can be just as performant (or as we saw, even more performant) for IO code should not be surprising for anyone who knows how stuff works at the lower levels. BTW this irrational "async is always webscale" crap has been happening in the Java community a…

> With Twisted I had to go find for Twisted versions of drivers for databases. Now for asyncio I would have to look for asyncio version of libraries. Isn't this an argument in favour of asyncio (in as much as it is/becomes the "one blessed/stlib interface")? Migration will take time, of course. But that is the nature of change...

Agreed that is what I like about it. However it came a bit too late and it will force fragmentation of the libraries even more.

Guido at the time was going through an "async" phase and was listening to Twisted developers and perhaps looking at the cool party across the street that Node.js was having and he didn't listen or entertain much of any other alternatives (supporting PyPy with STM, supporting or mainlining greenlet based approaches like eventlet and gevent -- libraries that are probably most commonly used for concurrent programming in Python).

Re: Asynchronous Python and Databases

#57
post #51

Async has never been about speed. Cooperative multitasking doesn't magically gzip your instruction pipeline or something (actually CM is suboptimal). It's just the only tool for those of us who want to avoid giving the keys to arguably the most important subsystem in the kernel to agents outside our control: The scheduler. Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed…

"Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed to log in, it'll be very very difficult to get htop to execute when it's competing with 2k other processes for cpu time." Nothing about your comment is compatible with my experience. It is certainly not true that a Linux box with 2000 threads blocked on i/o will be having any sort of bad time. If you're really got 2000 th…

> It is certainly not true that a Linux box with 2000 threads blocked on i/o will be having any sort of bad time.

You and I must have very different perceptions about the way a server is "having a bad time" :)

I was assuming they were at various stages of processing an incoming request, which means they were blocked on either legitimate disk i/o or swapping. It's very difficult to log in even locally in that case, because the login process can't read /etc/passwd in time.

> Also I'm not sure why you think that the kernel thread scheduler is all that good, or why it shouldn't be considered "outside our control.

I was just trying to say that it's dangerous to give non-trusted peers big influence on the way the scheduler behaves.

Re: Asynchronous Python and Databases

#58
post #41

Earlier quoted context omitted.

I've definitely seen the first - probably the first major performance issue that everyone runs into when using an ORM. By "N rows", do you mean having a view that only renders a constant number of rows, but your database query returns an larger collection that's then filtered in Python code? That seems like an obvious bug that can be usually fixed with a LIMIT clause.

LIMIT/OFFSET isn't the optimal way to do pagination either, because the database has to scan to offset before fetching the limit. I'm having a hard time trying to find the particular resource that dives into it, but here are the basics: SELECT * FROM t OFFSET 1000 LIMIT 10; -- does not use index, scans table vs SELECT * FROM t WHERE id between 1000 and 1010; -- uses index Edit: http://www.slideshare.net/Eweaver/effic…

A simple way to implement fast pagination is to set item.idx whenever you insert an item into a collection. It starts at 0 and gets incremented each time.

From there, given the number of items per page, you can trivially determine which items to display given a page number or which page to display given an item.idx. And the query uses the efficient WHERE.

Of course, the last_seen pagination is only usable for pagination where users aren't deep linking into pages (like HN).

Re: Asynchronous Python and Databases

#59
post #54

Zeek, I've been lucky enough to have benefitted from Sqlalchemy and Mako (but it's been awhile). Thanks. This article looked like it was going to hit the sweet spot of stuff I'm curious about, but I found I was still left with questions. If you (or anyone) will indulge me... I'll try and ask a question to help clarify matters. I work at a University on legacy ERP system(s). During registration there are 800+ concurre…

if you want to send out a series of long-reply SQL calls and wait for them all in batch, that is doable with Postgresql's async support, but they'd all be on distinct database connections, so you wouldn't get transactional consistency between these calls, but maybe that's not important. You can do the same thing with threads but it would mean you'd need to spin up that many threads, but at least would be something you could test in the short term to see if it is in fact feasible.

The rudimental SQLAlchemy-emulation system within aiopg right now can probably accommodate this use case but it is Postgresql specific. "Legacy ERP system" sounds like there's some different database in play there, if you are relying upon closed-source drivers you'd have to get access to a non-blocking API within them. Else you're stuck with threads.

acveilleux's point about caching here is very relevant and I thought of that also. if these are indeed independently read sets of fairly non-changing data, pulling it from a cache is the more traditional approach to reducing latency (as well as the need for 800+ database connections).

Re: Asynchronous Python and Databases

#60
post #51

Async has never been about speed. Cooperative multitasking doesn't magically gzip your instruction pipeline or something (actually CM is suboptimal). It's just the only tool for those of us who want to avoid giving the keys to arguably the most important subsystem in the kernel to agents outside our control: The scheduler. Ever tried to ssh into a one-thread-per-connection setup under heavy load? Assuming you managed…

> Cooperative multitasking doesn't magically gzip your instruction pipeline or something (actually CM is suboptimal).

There are multiple meanings of speed in a concurrent system. Is it throughput of each individual connection? Average throughput? Is it minimul latency, is it average latency?

What cooperative multi-tasking does is let all tasks make progress. Without having to have an explicit scheduling algorithm in each individual task.

There could be many reasons why ssh-ing migth be slow in a one-thread per connection. It could be that memory is low. Maybe the machine is over capacity. Maybe it is good that your ssh is slow because some higher business priority data is being transferred.

Thread also doesn't necessarily mean OS threads. Threads could be mean green threads (as in Python greenlet co-routines), it could be Go's channels, it could be Erlang's processes. Those are N:M threading models.

For example WhatsApp was running with 2M concurrent TCP connection on their FreeBSD servers say 3-4 years ago. Each connection with a separate lightweight process. So the logical model works well. It is the platforms and langauges that are behind so to speak.

Post reply on HN