Faster Database Testing with the New `tmp-postgres`

updated 12/28/19

tmp-postgres has hit 1.0.0.0 … more like blown past 1.0.0.0. The latest version is 1.34.0.0 as I write this.

tmp-postgres is an easy and fast way to make a temporary postgres database. Here is an example using with:

with $ \db -> bracket
  (connectPostgreSQL (toConnectionString db))
  close $
  \conn -> execute_ conn "CREATE TABLE foo (id int)"

The latest version brings with it a host of improvements. It is faster, more configurable and has a better API. It is a small, simple package but I have a lot to say about it. Performance improvements are the most fun, so let’s start there.

Doing Less is Faster

tmp-postgres 0.3.0.1 is the last “old” version before the rewrite that led to 1.0.0.0. Its way of starting and stopping postgres is our baseline.

tmp-postgres was based heavily on pg_tmp. Like pg_tmp it created a test database using createdb by default. However for most purposes this is not necessary. initdb creates a postgres database we can use for testing.

Baseline (sec) No createdb step (sec)
macOS 1.44 1.11
Ubuntu guest 0.608 0.546

Faster Setup with initdb caching

Before an ephemeral postgres process can start a temporary database a cluster needs to be created. This means creating a temp directory and calling initdb with the appropriate arguments. This is the slowest part of a naive tmp-postgres startup.

However for a given initdb version and inputs the execution is referentially transparent so we can cache the output of initdb. This works great in practice because the input to initdb rarely changes so the data cached is small.

No caching (sec) With initdb caching (sec)
macOS 1.11 0.353
Ubuntu guest 0.546 0.100

COW is Faster

The start up time is now mostly copying the cached cluster. Many of the files in the cluster are not modified during the duration of a test. On newer operating systems we can use “copy on write” to make the copy faster.

No COW (sec) COW (sec)
macOS 0.353 0.248
Ubuntu guest 0.100 0.092

Of the remaining time, around 80% is the copy and 20% shutting down postgres.

Final Results

Start (sec) End (sec) Improvement
macOS 1.44 0.248 5.8x
Ubuntu guest 0.608 0.092 6.6x

I’ve found the improved performance is large enough to be felt in real projects. I saw a 2x improvement in test time when porting postgresql-simple-queue to the latest tmp-postgres version.

But Wait, There’s More!

The new tmp-postgres is over 5x faster on macOS and linux but the story doesn’t end here. In parts 2 and 3 of this blog post series I’ll show how to use additional features of tmp-postgres to keep database testing fast as the size of your project grows.

Home | Part 2