3 ms·
Sorry, the points you make here are all very much like the FUD I read about ORMs - all based on experiences with inferior software, not an approach. > 1. It ke
by zzzeek 14y ago
Sorry, the points you make here are all very much like the FUD I read about ORMs - all based on experiences with inferior software, not an approach.
> 1. It keeps open N connections to the database for each one of your M server processes. This is especially bad in Postgresql, as that means you have N * M idle processes on the database server.
Suppose you use PGBouncer, and you configure it to allow 50 connections. Or, you configure your web servers to allow 10 concurrent processes each with 5 connections. Total number of connections is...the same !
> 2. It is very easy to improperly reset a database connection before giving it back out of the pool. This leads to two problems.
It is actually not possible at all when using a pool like that of SQLAlchemy's, as any connection that gets returned to the pool is properly reset by the pool itself.
> problem 1: The first is that if code using a db connection has an error and doesn't issue a rollback, the connection will still be in a bad state when it is handed back out of the pool again.
The pool emits the ROLLBACK whether or not the application did so. Problem solved.
> problem 2: It is inevitable that somewhere in your code you will not issue a rollback on a connection after starting a transaction.
It would be crazy to leave connections idle in the pool with transactional state on them. No connection idle in the pool has transactional state, as they do not get returned without a rollback. If a catastrophic error occurred on the connection, it's tossed out of the pool. This is all detected at a layer of abstraction below what the application sees, so the programmer has no chance of messing it up.
> In fact, if you are using Python the default dp-api behavior is to "transactional mode" where a BEGIN is sent immediately before any query on a new connection. It will automatically start the transaction, but it will not COMMIT or ROLLBACK automatically.
this is all the same non-issue.
> If you use a server like gunicorn, or a library like multiprocessing, open file handles (including sockets) are shared upon fork(). This means that if you aren't very careful to completely remove and recreate all the connections in your connection pool after a fork, you will have multiple processes sharing the same sockets. The processes will then step all over each other and corrupt each others use of the database.
right, that's why multiprocessing always provides hooks for an init function that runs right as the new child process starts. In this function, you basically create a new pool (or reset the one you have). I'd disagree with the characterization that one must be "very careful to completely remove and recreate all the connections", this makes it sound like some dramatic programming effort; it's not. It's called pool.dispose(), it's designed for this case, and it does it in one step.
SQLAlchemy's pool is designed such that it doesn't pre-allocate connections, so if your subprocess only uses one connection, the pool will only do so as well.
It's true that in this scenario, you're losing the usual benefits of a pool if your subprocess lives for only the length of a single transaction, but it doesn't make things worse, as long as you remember to start up the new pool across process boundaries.
> In application pools of database connections is something that will bite you over and over
its obvious you've used some very poorly written pooling solutions, but the good news is that these problems have been solved. PGBouncer is great but isn't strictly necessary, and also can be combined with in-app pools. Only an in-app pool gives you the means to limit the connections used by an individual process (as I mentioned elsewhere, if PGBouncer allows this, it would have to be through a more complex and inconvenient system of configuring individual servers to connect differently so that they can be differentiated). SQLAlchemy's pool is deployed in thousands of production environments including some big ones like Reddit, Yelp and Dropbox. There is no issue.
- justin_vanw 14y agoEDIT: I had written a really long and specific reply, full of bombast and insults. EDIT2: Hey zzzeek, sorry you saw that before I edited it. I realized my mistake about the 2012 thing, and then when I re-read what I had written I realized it was over the top. PGBouncer has all the features that any in application connection pool has, and it doesn't have any of the pitfalls. It's easier to set up, and once set up gives you lots of benefits that an in-application pool cannot give you. Your example stating that the total number of connections is the same is just incorrect. PGBouncer shares connections between processes, and will release connections beyond it's minimum gradually if they aren't used within a few minutes. SQLAlchemy's connection pool doesn't release connections below it's minimum, ever. If there is a load spike, SQLAlchemy's pool will either become a bottleneck, or will create more connections as needed, which are immediately destroyed and recreated if the traffic spike isn't completely uniform (say if it goes up and down even a little). If I said to you, you have two choices. They both take about the same amount of effort to set up, except one performs far better than the other, consumes less resources, and gives you lots of other benefits, why would you choose the lesser option?
- zzzeek 14y agoEdit: apparently as I finished typing this, the parent post edited itself out...though there's some decent info about how to reset sessions here. I'll take the more heated quotes/responses out. > However, lets say you use the SQLAlchemy pool's overflow setting. In that case, new connections will be made as needed, except they will be discarded as they go back to the pool, if the pool has 10 back in it already. Since connections aren't shared between processes, this means many, many new connections will be made and discarded, adding a sizable amount of extra load to a database server already heavily taxed by the high level of traffic. That's the key, that the app is going to be using N number of connections constantly, and the "max overflow" is used for spikes. Nothing is stopping you from combining the in app pools with PGBouncer if you're optimizing for this particular case. There's nothing wrong with PGBouncer. But there's also nothing wrong with in-app pools. > Nothing, the PGBouncer solution is superior in every respect. the two approaches are not mutually exclusive. >> The pool emits the ROLLBACK whether or not the application did so. Problem solved. > 2. Really? When did you add this feature? in 2007. Were you using SQLAlchemy in version 0.2 or 0.3 or something? That would be pretty unfortunate, it was more or less alpha software at that time. > I see it, it is called reset_on_return, and was added to SQLAlchemy in version 0.7.6, which was released in 2012. You are incorrect. reset_on_return is a setting that allows the behavior to be configurable - before that, it was on in all cases. Users of MySQL with MyISAM wanted to turn it off as they weren't using transactions anyway and it was taking up time they didn't want to spend. > Also, ROLLBACK is completely insufficient here, as things like temporary tables, query planner variable changes, and many other very common things persist on the SESSION level. Those are unusual and exotic cases, which if one were using (haven't seen too many web apps that create temporary tables) would have to be planned for. If someone really wants to make session level changes, we have an event system by which those changes can be reset when the connection's state is finished. I've gone and checked PGBouncer's documentation - they deal with it in exactly the same way, which is that you need to set up your own query using "server_reset_query". > Additionally, SQLAlchemy doesn't check that the database connection is valid before letting it out of the pool, just when it is returned. by default this would waste a lot of time. But if you want to do that, here is the documentation to enable that feature: http://docs.sqlalchemy.org/en/rel_0_8/core/pooling.html#disconnect-handling-pessimistic http://docs.sqlalchemy.org/en/rel_0_8/core/pooling.html#disc... > If the database is restarted, SQLAlchemy will gladly hand you database 'connections' where the TCP connection has been closed. if you're not using pessimistic handling (which most people don't), you will get at most one of these. the entire pool is flushed when this condition is detected. > Right. Or you could just use something like PGBouncer, and not have to do this. Also, PGBouncer will have it's connections pooled and ready to go across application restarts, forks, whatever. so use PGBouncer as well. Nothing is wrong with PGBouncer, it's simply not universally necessary. > That is something that has never come up for me, and honestly I think it is YAGNI. honestly that's what I think about your entire first paragraph, the vast majority of apps don't really need that level of detailed tuning. The app servers all hold onto a handful of connections, make some new ones as needed, and it works just fine. But I won't say that your scenario doesn't have merit, partially because it's too late at night for me to parse all that and it's obvious that very high performance scenarios can benefit from having more automation at more levels, nobody is saying PGBouncer is a bad product. I've just never had to bother installing/starting/configuring a whole new daemon just to have a medium size web application run adequately. > Well, there are a bunch of issues: worse performance, many more database connections that just sit idle (doesn't matter with mysql, matters a lot with postgres), If the applications are idle, then you'd have idle connections, but I haven't experienced that being an issue - the database is idle too ! If you have some servers spiking and other ones idling, then its an issue, so use PGBouncer for that case. But that's not typical in my experience.