Enable Redis cache key compression
We have a number of keys that are over 1 MB that would be compressed significantly (over 90%) if this feature were enabled. We had this disabled by default to ensure backwards compatibility with the previous Redis store.
We should enable it now because compression significantly reduces the pressure on the Redis server. Every time a large payload is stored in Redis, Redis may need to evict many small keys to make room.
This adds a little more CPU overhead on the application workers, which
is why we have an environment variable
(ENABLE_REDIS_CACHE_COMPRESSION
) to turn if off it it becomes a
problem.
Behind the scenes, Rails implements this by serializing a compressed
variable with the ActiveSupport::Cache::Entry
if the payload size exceeds the
compression threshold (4K by default). When the entry is deserialized
from Redis, if the compressed
boolean is set, the value will be
decompressed. Thus even in a mixed-deployment where this setting is not
on by default, Rails will be able to decode compressed values just fine.
Closes #198586 (closed)
Example
[2] pry(main)> data = File.read('/tmp/parsed.txt'); nil
=> nil
[3] pry(main)> Rails.cache.write('test-compress', data); nil
=> nil
[4] pry(main)> Rails.cache.read('test-compress').length
=> 5194491
[5] pry(main)> Gitlab::Redis::Cache.with { |redis| redis.get('cache:gitlab:test-compress') }.length
=> 148086
The read also works when the config variable is disabled.
Benchmarks
Summary
Read performance
I tested read/write performance in three cases:
- Staging
- My local GDK
- Local Omnibus instance
Test | Compressed? | Iterations/s |
---|---|---|
GDK | Yes | 62.5 |
Omnibus | No | 36.8 |
Omnibus | Yes | 36.7 |
Staging | Yes | 34.6 |
Staging | No | 26.0 |
GDK | No | 12.3 |
Write performance
Test | Compressed? | Iterations/s |
---|---|---|
Staging | No | 30.8 |
GDK | Yes | 19.3 |
Staging | Yes | 14.2 |
Omnibus | Yes | 9.2 |
GDK | No | 7.0 |
Omnibus | No | 1.2 |
As we can see, compression usually speeds up reads, presumably because there are fewer network hops, but is a little slower on writes. I'm not sure why the times were roughly equivalent in the Omnibus case. The iterations/s comparison across tests may not be as important as the differences within the same machine.
Read performance (staging)
require 'benchmark/ips'
data = File.read('/tmp/parsed.txt')
Rails.cache.options[:compress] = true
Rails.cache.write('test1', data)
Rails.cache.options[:compress] = false
Rails.cache.write('test2', data)
Benchmark.ips do |x|
x.report("compressed") {
Rails.cache.read('test1')
}
x.report("uncompressed") {
Rails.cache.read('test2')
}
x.compare!
end
Calculating -------------------------------------
compressed 3.000 i/100ms
uncompressed 2.000 i/100ms
-------------------------------------------------
compressed 34.566 (± 2.9%) i/s - 174.000
uncompressed 26.046 (±23.0%) i/s - 124.000
Comparison:
compressed: 34.6 i/s
uncompressed: 26.0 i/s - 1.33x slower
Write performance (staging)
However, write performance is 2x slower:
require 'benchmark/ips'
data = File.read('/tmp/parsed.txt')
Benchmark.ips do |x|
x.report("compressed") {
Rails.cache.options[:compress] = true
Rails.cache.write('test1', data)
}
x.report("uncompressed") {
Rails.cache.options[:compress] = false
Rails.cache.write('test2', data)
}
x.compare!
end
Calculating -------------------------------------
compressed 1.000 i/100ms
uncompressed 3.000 i/100ms
-------------------------------------------------
compressed 14.198 (±21.1%) i/s - 68.000
uncompressed 30.807 (± 3.2%) i/s - 156.000
Comparison:
uncompressed: 30.8 i/s
compressed: 14.2 i/s - 2.17x slower
Local benchmarks
Read
Calculating -------------------------------------
compressed 5.000 i/100ms
uncompressed 1.000 i/100ms
-------------------------------------------------
compressed 62.534 (±17.6%) i/s - 305.000
uncompressed 12.306 (±65.0%) i/s - 47.000
Comparison:
compressed: 62.5 i/s
uncompressed: 12.3 i/s - 5.08x slower
Write
Calculating -------------------------------------
compressed 1.000 i/100ms
uncompressed 1.000 i/100ms
-------------------------------------------------
compressed 19.297 (±15.5%) i/s - 94.000
uncompressed 6.975 (±14.3%) i/s - 35.000
Comparison:
compressed: 19.3 i/s
uncompressed: 7.0 i/s - 2.77x slower