Large documents in redis: is it worth compressing them (Part 1)
Tags: redis, compression, performance, benchmark
Published: 2021-05-28 15:36:21

At <a href="https://www.forestadmin.com" target="_blank">Forest Admin</a>, we build admin panels for which we need to compute and cache <strong>large JSON documents</strong>. These documents are stored in <strong>redis</strong> and retrieved from this storage in order to be as fast as possible.

## 🐘 The problem with large JSON documents: latency

Some of these JSON document can weight more than 20MB. Storing and retrieving such large document introduce latency in our services, as the data needs to be uploaded or downloaded through the network.

So, every time we need to retrieve these documents from the server, we download 20MB (in worst cases) from redis and parse the received json. 

Looking at performance logs in production, I could see that this operation of storing or retrieving these documents costed us some significant amount of time.

I wanted to test if would worth to upload compressed json documents instead of plain json content in redis.

That's why I created [a repository on github](https://github.com/ghusse/evaluate-redis-compression) with some code to **run benchmarks**.

## 🕗 Protocol

I want to evaluate the total time of pushing a document and retrieving it from redis, with 5 different implementations:

- `none`: `JSON.stringify()` + push the result to redis
- `brotli`: `JSON.stringify()` + `brotli` + push the buffer to redis
- `deflate`: `JSON.stringify()` + `deflate` + push the buffer to redis
- `gzip`: `JSON.stringify()` + `gzip` + push the buffer to redis

- [`msgpack`](https://msgpack.org/index.html): `msgpack.pack()` + push the buffer to redis

Each compression algorithm will be evaluated with every possible compression level:
- `brotli`: 1 to 11
- `deflate`: -1 to 9
- `gzip`: -1 to 9

Json documents will be retrieved from a redis server, based on a key pattern. For my own test, I will use **real JSON documents** of various sizes that we handle in production.

The benchmark is run against a redis server, and in my case, it'll be a **local server running in docker**.

Finally, these tests are run on my Macbook Pro 2,8 GHz Intel Core i7, on node 14.

## 📈  Results

### Brotli compression

![Overall download duration with brotli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1dixlifyhnffsea0ozh.png "Overall download duration with brotli")

We can see that a compressed document with brotli will always be faster to retrieve, and decompress than a large uncompressed document.

![Overall upload duration with brotli](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/33aoha56r8imaq90ancd.png "Overall upload duration with brotli")

These first results show that **⚠ brotli is very slow** during compression, when used with quality levels of **10** and **11**. That's something worth noting as **11 is the default value** in node 14.

Using **brotli with its default value** is actually slower than not doing anything for **document upload**.

I plotted another graph without these 2 values for brotli compression, to be able to compare quality levels.
 
![Overall upload duration with brotli (without 10 and 11)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00w071apo7g1n0cgw8i4.png "Overall upload duration with brotli without quality levels of 10 and 11")

With this 3<sup>rd</sup> plot, we can see that any other quality level allows to have **better upload time** of our documents to redis.

We also can see that the minimal compression level has the best possible performance in the conditions of my experiments. It might change for instance with a remote redis server.

### To summarize

- Download:
  - 🏅 brotli-11 for download performance
  - ✅ every compression level has better performance
- Upload:
  - 🏅 brotli-1 for upload performance
  - ✅ compression levels up to 9 are faster than no compression
  - ❌ brotli-10 and brotli-11 are **slower** than no compression
- Potential winner:
  - 🏅 **brotli-1**

## Deflate compression

![Overall download duration with deflate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1diomgay3ozgytmvfp5.png "Overall download duration with deflate")

Same results here for deflate than brotli: it's always faster to retrieve compressed documents from redis when they are compressed.
 
![Overall upload duration with deflate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3bw9uzj2saauoel2mb7.png "Overall upload duration with deflate")

On the other hand, it's also always faster to upload compressed documents to redis with deflate, whatever the compression level we choose.

We can see that <strong>deflate 3</strong> seems to be the faster when used to upload documents.

### To summarize

- Download:
  - ✅ Results are very similar across compression levels, and we cannot spot a clear winner from the results
- Upload:
  - 🏅 deflate-3 seems to be slightly faster than other compression levels
  - ✅ all compression levels are faster to use than nothing at all
- Potential winner:
  - 🏅 **deflate-3**

## Gzip compression
 
![Overall download duration with gzip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dji8ecbwy20q3o4zfag.png "Overall download duration with gzip")

With gzip also, it's always faster to retrieve compressed documents from redis than uncompressed documents.
 
![Overall upload duration with gzip](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sq9pe6y18q83xz4cie32.png "Overall upload duration with gzip")

Using gzip to upload documents is also always faster, but we can see a clear difference between stronger compression levels (8 & 9) that seem a little slower.

### To summarize

- Download:
  - ✅ Results are very similar across compression levels, and we cannot spot a clear winner from the results
- Upload:
  - 🏅 gzip-3 seems to be slightly faster than other compression levels
  - ✅ all compression levels are faster to use than nothing at all
- Potential winner:
  - 🏅 **gzip-3**

## msgpack

The library [`msgpack`](https://msgpack.org/index.html) allows to serialize javascript objects in a smaller form that the standard `JSON.stringify`. It is documented as slower than JSON, but I wanted to measure if the time to serialize was compensated by a faster transfert.

It also has the advantage to transform objects into a buffer in one operation, in contrary to the solution using a compression algorithm:
- msgpack transforms an object into a buffer
- solutions with compression are requiring 2 steps: one for transformation to JSON, another for the compression of the resulting string.
 
![Overall download duration with msgpack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqbysisf6elbu6zaiac2.png "Overall download duration with msgpack")

Results are not good for msgpack in my local environment: it seems **always slower to use msgpack** than just sending plain JSON documents to my redis server.

⚠ This library can still produce interesting results when the network gets slower. In my situation, it cannot be faster than solutions based on compression, because the compression level obtained with msgpack (around 60%) is a lot less interesting than the compression obtained with compression algorithms (more than 90%).
 
![Overall upload duration with msgpack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n604o0a8ns80aeish865.png "Overall upload duration with msgpack")

Upload results are not good either, for msgpack.

### To summarize

❌ msgpack is slower than the original implementation both for upload & download

## Final match: brotli-1 vs deflate-3 vs gzip-3

![Overall download duration with best compression algorithms](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5arwwzdw51emn2zzfba.png "Overall download duration with the best compression algorithms")

Results here are very similar between compression algorithms.

![Overall upload duration with best compression algorithms](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwvvgkjj81wimlarye18.png "Overall download upload with the best compression algorithms")

Regarding upload time, **brotli-1** seems to have a clear advantage over other algorithms.

## And the winner is: 🏅 **brotli-1**

Next step: validate these results on an solution that is closer to the production:
- on an instance that has similar performance to production instances
- use a remote redis instance, similar to the one used in production.

These results will be shared in a part 2 of this series!