Scaling PostgreSQL at Braintree: Four Years of Evolution
braintreepayments.com
braintreepayments.com
I think what they meant to say is that unlike most web applications, they are heavier on writes than reads.
Write-heavy workloads -- aka OLTP -- are the bread and butter of relational databases. To the point that people complain they aren't as good at read-heavy workloads.
Credit card transactions are literally the textbook example of OLTP.
Now I'm inspired to start up a few VMs and have a play with the gems they mentioned, and try to get replication and failover happening.
Also, people, please stop trying to do "automatic failover". There is no such thing, in the total sense. You have to have humans checking to make sure everything ok, and lots and lots of tripwires to prevent one of a multitude of catastrophic problems from occurring. Just automate pieces in totally reliable and non-harmful ways and let a human oversee the cut-over.
We were reluctant to do automatic failover as well, since we all hear horror stories about automatic failover causing more problems than it solves. However, we changed our minds this summer for a couple of reasons. One, our traffic continues to grow and people rely on us for payments. If our database server crashes in the middle of the night, we don't want our customers to have to wait for us to do manual failover. Two, we felt a lot more comfortable with Pacemaker and PostgreSQL streaming replication failover than we used to with DRBD. We did extensive testing and tweaking, and we believe we understand the failure scenarios.
There are many axes on which you can scale and many different interpretations. People have reported that postgres scales very well to many cores and many concurrent users (something like 300K read queries per second or something ridiculous[1]). It also scales nicely for concurrent writes and avoids locking in situations where most other databases don't.
There may be cases where postgres adapted plans (automatically using statistics and the cost-based optimizer, of course) for them as their data grew. Responding to data growth is a kind of scaling -- if using a database without that capability, it may be stuck with a horrible plan that falls over as the data grows.
Scaling to multiple servers is, no doubt, important; but it's not the only important definition of "scaling" (if it were, then multi-core would just be a fad). Lots of technologies scale to many servers beautifully, but lose focus on other aspects of scalability or are just plain inefficient.
[1] http://rhaas.blogspot.com/2012/03/performance-and-scalabilit...
Maybe I've implemented things incorrectly, but this is a frustrating issue for the moment.
Edit: We also don't increment the timeline on failover. Instead, we stop PostgreSQL, remove the recovery.conf, and restart.
I didn't feel safe blatting the new primary back to the secondary till we had the third level in there, since once you've failed over, you're at the mercy of that one machine and your latest backup is on the one that you're overwriting.
The third level one will follow the change in the recovery timeline if it's incremented in recovery.conf, and you make sure that it gets the appropriate history file.
There is a patch to go into 9.3 I believe which allows you to handle timeline shifts through walsender instead of the archive. We're using gluster for the shared archive at the moment and it will be nice to reduce reliance upon it.
Thanks for the useful answer.
One of the advantages is that it can help you identify queries that are both very short but frequent (and could use a cache, or whatnot), and would fly below the radar of most log aggregations, because most people turn off logging off for very short statements. See also: identifying n+1 query pathologies.
It really blows away log analysis and pg_fouine for all but the most heavyweight tasks, and its approach is a lot more sound than log analysis tools because it uses the semantic representation -- not the syntactic one -- of the query submitted.
It allows you to get statistics per top-level function, which can reveal abnormalities otherwise impossible to detect, such as a function which is usually very fast, but very slow for a small number of calls relative to the total number of calls. Consider fa()->fx() and fb()->fx(), if fa()->fx() always is fast and you have a million such calls, but for some reason fb()->fx() is very slow, but you only have a few such calls, fx() will look like a very fast function without any performance problems, if you only look at pg_catalog.pg_stat_user_functions, but call_graph will reveal fx() being slow when called by the top-level function fb().
This is only relevant for systems always accessing the database through Stored Procedures though.
Conservatism is the watchword when $$$ are involved.