Lightning fast integration tests with Docker, MySQL and tmpfs

Integration tests that involve database operations requite to tear down and re-initialize a database multiple times. Although most developer machines offer SSD and enough RAM, database initialization can consume a considerable amount of time nevertheless.

Docker allows defining volumes that are mounted directly into the memory by using tmpfs. We can utilize this feature to utilize the speed up disk operations as during database imports, by moving the data from the disk into the memory.

The following example measures the time for writing 1GB worth of data to an SSD:

dd if=/dev/zero of=/tmp/output bs=1024k count=1024; 
1024+0 Datensätze ein
1024+0 Datensätze aus
1073741824 bytes (1,1 GB, 1,0 GiB) copied, 2,22033 s, 484 MB/s

For comparison, the following steps create a RAM disk and write the data to memory.

$ sudo mount -o size=1G -t tmpfs none /tmp/tmpfs

$ dd if=/dev/zero of=/tmp/tmpfs/output bs=1024k count=1024; 
1024+0 Datensätze ein
1024+0 Datensätze aus
1073741824 bytes (1,1 GB, 1,0 GiB) copied, 0,309017 s, 3,5 GB/s

As you can see writing 1GB to memory is 7x faster. With the following Docker run command, you can spin-up a default MySQL container, where the data directory resides in a tmpfs.

docker run -d \
    --rm \
    --name mysql-56-in-memory \
    -p 3307:3306 \
    --tmpfs /var/lib/mysql:rw,noexec,nosuid,size=1024m \
    -e MYSQL_ALLOW_EMPTY_PASSWORD=TRUE \
    -e MYSQL_DATABASE=dev \
    mysql:5.6

The arguments of Docker run mean the following

  • –rm: delete the container once it was stopped
  • –name: a name for the container
  • -p: map the host’s port 3307 to the port 3306 inside the container. This allows to run multiple MySQL containers in parallel and connect to them from the host via the port specified
  • –tmpfs: This line mounts the internal directory of the container to a RAM disk. It should be writeable (rm). Noexec prevents the execution of binaries, nosuid prevents changing the permission flags and the size specifies the size occupied by the tmpfs partition in memory. Adapt this to your usecase. The minimum for MySQL is around 200MB. Add the space needed for your data, indices etc.
  • MYSQL_ALLOW_EMPTY_PASSWORD does what it implies
  • MYSQL_DATABASE defines the name of a database to be created

If you run this command you can connect to the container like this: mysql -u root -h 127.0.0.1 -P 3307

The container behaves like a normal MySQL database, unless the data is not persisted on a hard disk, but only stored in the ephemeral memory. If you stop the container, it will be removed by docker and if you reboot the machine the data will be gone. for obvious reasons this is only a good idea for test data that can be re-created at any time.

You can achieve the same also with Docker Compose if you would like to orchestrate multiple containers.

version: '3'
services:
  mysql-56-integration:
    container_name: mysql-56-in-memory
    restart: unless-stopped
    image: mysql:5.6
    environment:
      - MYSQL_ALLOW_EMPTY_PASSWORD='true'
      - MYSQL_HOST=''
    volumes:
      - data:/var/cache
    ports:
      - "3307:3306"
 
volumes:
  data:
    driver_opts:
      type: tmpfs
      device: tmpfs

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.