rails-brotli-cache 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +23 -3
- data/benchmarks/Gemfile +2 -0
- data/benchmarks/main.rb +64 -4
- data/lib/rails-brotli-cache/store.rb +8 -3
- data/lib/rails-brotli-cache/version.rb +1 -1
- data/rails-brotli-cache.gemspec +1 -1
- data/spec/rails-brotli-cache/compatibility_spec.rb +9 -6
- data/spec/rails-brotli-cache/store_spec.rb +15 -2
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e109b9b356474fb8085b1cceaa892695de997465db4c90872556f76419da8f86
|
4
|
+
data.tar.gz: c8422310e9fed160052637a40dc7b1150b938c0f891b3b7b011eacdd11b8e4bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b435443fbf3a6a09baed87b124429e43dcd4d3fbd06cab175a15818b397285aeab96ad0694dc8a2f0e0d91c3478400ba0e3081045e93ca63e275e62626f0e0d
|
7
|
+
data.tar.gz: 95af20d56721fbe1e9118b29a0fa1f7dc3fe5825d8c7ee04d77b783b96b405d6b1e765f4d8b63e8668d9414ca1e716cc010e3201d7a93340d35251ebd966f9e8
|
data/README.md
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
# Rails Brotli Cache [](https://badge.fury.io/rb/rails-brotli-cache) [](https://badge.fury.io/rb/rails-brotli-cache) [](https://github.com/pawurb/rails-brotli-cache/actions)
|
2
2
|
|
3
3
|
This gem enables support for compressing Ruby on Rails cache entries using the [Brotli compression algorithm](https://github.com/google/brotli). `RailsBrotliCache::Store` offers better compression and performance compared to the default `Rails.cache` Gzip, regardless of the underlying data store. The gem also allows specifying any custom compression algorithm instead of Brotli.
|
4
4
|
|
5
|
-
|
5
|
+
You can check out [this blog post](https://pawelurbanek.com/rails-brotli-cache) describing the gem in more detail.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
`Gemfile`
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'brotli' # an optional dependency, other compressors are supported
|
13
|
+
gem 'rails-brotli-cache'
|
14
|
+
```
|
6
15
|
|
7
16
|
## Benchmarks
|
8
17
|
|
@@ -130,10 +139,21 @@ config.cache_store = RailsBrotliCache::Store.new(
|
|
130
139
|
```
|
131
140
|
|
132
141
|
```ruby
|
142
|
+
|
143
|
+
class ZSTDCompressor
|
144
|
+
def self.deflate(payload)
|
145
|
+
::Zstd.compress(payload, 10)
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.inflate(payload)
|
149
|
+
::Zstd.decompress(payload)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
133
153
|
Rails.cache.write('test-key', json, compressor_class: Snappy)
|
134
154
|
```
|
135
155
|
|
136
|
-
This config expects a class
|
156
|
+
This config expects a class that defines two methods, `inflate` and `deflate`. It allows to use, for example, a [ZSTD by Facebook](https://github.com/SpringMT/zstd-ruby), offering even better performance and compression.
|
137
157
|
|
138
158
|
## Testing
|
139
159
|
|
data/benchmarks/Gemfile
CHANGED
data/benchmarks/main.rb
CHANGED
@@ -1,30 +1,62 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
3
|
require 'net/http'
|
4
|
+
require 'brotli'
|
4
5
|
require 'rails-brotli-cache'
|
6
|
+
require 'benchmark'
|
7
|
+
require 'zstd-ruby'
|
8
|
+
|
9
|
+
class ZSTDCompressor
|
10
|
+
def self.deflate(payload)
|
11
|
+
::Zstd.compress(payload, 10)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.inflate(payload)
|
15
|
+
::Zstd.decompress(payload)
|
16
|
+
end
|
17
|
+
end
|
5
18
|
|
6
19
|
memory_cache = ActiveSupport::Cache::MemoryStore.new(compress: true) # memory store does not use compression by default
|
7
20
|
brotli_memory_cache = RailsBrotliCache::Store.new(memory_cache)
|
21
|
+
zstd_memory_cache = RailsBrotliCache::Store.new(memory_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
|
22
|
+
|
8
23
|
redis_cache = ActiveSupport::Cache::RedisCacheStore.new
|
9
24
|
brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
|
25
|
+
zstd_redis_cache = RailsBrotliCache::Store.new(redis_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
|
26
|
+
|
10
27
|
memcached_cache = ActiveSupport::Cache::MemCacheStore.new
|
11
28
|
brotli_memcached_cache = RailsBrotliCache::Store.new(memcached_cache)
|
29
|
+
zstd_memcached_cache = RailsBrotliCache::Store.new(memcached_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
|
30
|
+
|
12
31
|
file_cache = ActiveSupport::Cache::FileStore.new('/tmp')
|
13
32
|
brotli_file_cache = RailsBrotliCache::Store.new(file_cache)
|
33
|
+
zstd_file_cache = RailsBrotliCache::Store.new(file_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
|
14
34
|
|
15
35
|
json_uri = URI("https://raw.githubusercontent.com/pawurb/rails-brotli-cache/main/spec/fixtures/sample.json")
|
16
36
|
json = Net::HTTP.get(json_uri)
|
17
37
|
|
18
38
|
puts "Uncompressed JSON size: #{json.size}"
|
19
39
|
redis_cache.write("gz-json", json)
|
20
|
-
gzip_json_size = redis_cache.redis.
|
40
|
+
gzip_json_size = redis_cache.redis.with do |conn|
|
41
|
+
conn.get("gz-json").size
|
42
|
+
end
|
21
43
|
puts "Gzip JSON size: #{gzip_json_size}"
|
22
44
|
brotli_redis_cache.write("json", json)
|
23
|
-
br_json_size = redis_cache.redis.
|
45
|
+
br_json_size = redis_cache.redis.with do |conn|
|
46
|
+
conn.get("br-json").size
|
47
|
+
end
|
24
48
|
puts "Brotli JSON size: #{br_json_size}"
|
25
49
|
puts "~#{((gzip_json_size - br_json_size).to_f / gzip_json_size.to_f * 100).round}% improvment"
|
26
50
|
puts ""
|
27
51
|
|
52
|
+
zstd_redis_cache.write("json", json)
|
53
|
+
zs_json_size = redis_cache.redis.with do |conn|
|
54
|
+
conn.get("zs-json").size
|
55
|
+
end
|
56
|
+
puts "ZSTD JSON size: #{zs_json_size}"
|
57
|
+
puts "~#{((gzip_json_size - zs_json_size).to_f / gzip_json_size.to_f * 100).round}% improvment"
|
58
|
+
puts ""
|
59
|
+
|
28
60
|
iterations = 100
|
29
61
|
|
30
62
|
Benchmark.bm do |x|
|
@@ -42,6 +74,13 @@ Benchmark.bm do |x|
|
|
42
74
|
end
|
43
75
|
end
|
44
76
|
|
77
|
+
x.report("zstd_memory_cache") do
|
78
|
+
iterations.times do
|
79
|
+
zstd_memory_cache.write("test", json)
|
80
|
+
zstd_memory_cache.read("test")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
45
84
|
x.report("redis_cache") do
|
46
85
|
iterations.times do
|
47
86
|
redis_cache.write("test", json)
|
@@ -56,6 +95,13 @@ Benchmark.bm do |x|
|
|
56
95
|
end
|
57
96
|
end
|
58
97
|
|
98
|
+
x.report("zstd_redis_cache") do
|
99
|
+
iterations.times do
|
100
|
+
zstd_redis_cache.write("test", json)
|
101
|
+
zstd_redis_cache.read("test")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
59
105
|
x.report("memcached_cache") do
|
60
106
|
iterations.times do
|
61
107
|
memcached_cache.write("test", json)
|
@@ -70,6 +116,13 @@ Benchmark.bm do |x|
|
|
70
116
|
end
|
71
117
|
end
|
72
118
|
|
119
|
+
x.report("zstd_memcached_cache") do
|
120
|
+
iterations.times do
|
121
|
+
zstd_memcached_cache.write("test", json)
|
122
|
+
zstd_memcached_cache.read("test")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
73
126
|
x.report("file_cache") do
|
74
127
|
iterations.times do
|
75
128
|
file_cache.write("test", json)
|
@@ -83,4 +136,11 @@ Benchmark.bm do |x|
|
|
83
136
|
brotli_file_cache.read("test")
|
84
137
|
end
|
85
138
|
end
|
139
|
+
|
140
|
+
x.report("zstd_file_cache") do
|
141
|
+
iterations.times do
|
142
|
+
zstd_file_cache.write("test", json)
|
143
|
+
zstd_file_cache.read("test")
|
144
|
+
end
|
145
|
+
end
|
86
146
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support/cache'
|
4
|
-
|
4
|
+
begin
|
5
|
+
require 'brotli'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
5
8
|
|
6
9
|
module RailsBrotliCache
|
7
10
|
class Store < ::ActiveSupport::Cache::Store
|
@@ -112,8 +115,10 @@ module RailsBrotliCache
|
|
112
115
|
@core_store.fetch_multi(
|
113
116
|
*names, options.merge(compress: false)
|
114
117
|
) do |name|
|
115
|
-
compressed(yield(name), options)
|
116
|
-
end
|
118
|
+
compressed(yield(source_cache_key(name)), options)
|
119
|
+
end.map do |key, val|
|
120
|
+
[source_cache_key(key), uncompressed(val, options)]
|
121
|
+
end.to_h
|
117
122
|
end
|
118
123
|
|
119
124
|
def exist?(name, options = {})
|
data/rails-brotli-cache.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.license = "MIT"
|
18
18
|
gem.add_dependency "activesupport"
|
19
|
-
gem.
|
19
|
+
gem.add_development_dependency "brotli"
|
20
20
|
gem.add_development_dependency "rspec"
|
21
21
|
gem.add_development_dependency "railties"
|
22
22
|
gem.add_development_dependency "activemodel"
|
@@ -13,6 +13,10 @@ describe RailsBrotliCache do
|
|
13
13
|
]
|
14
14
|
|
15
15
|
CACHE_STORE_TYPES.each do |cache_store_types|
|
16
|
+
let(:big_enough_to_compress_value) do
|
17
|
+
SecureRandom.hex(2048)
|
18
|
+
end
|
19
|
+
|
16
20
|
describe "Brotli cache has the same API as #{cache_store_types[0].class}" do
|
17
21
|
subject(:brotli_store) do
|
18
22
|
RailsBrotliCache::Store.new(cache_store_types[0])
|
@@ -32,7 +36,7 @@ describe RailsBrotliCache do
|
|
32
36
|
|
33
37
|
it "for #read and #write" do
|
34
38
|
int_val = 123
|
35
|
-
expect(brotli_store.write("int_val_key",
|
39
|
+
expect(brotli_store.write("int_val_key", big_enough_to_compress_value).class).to eq(standard_cache.write("int_val_key", big_enough_to_compress_value).class)
|
36
40
|
expect(brotli_store.read("int_val_key")).to eq(standard_cache.read("int_val_key"))
|
37
41
|
|
38
42
|
str_val = "str"
|
@@ -55,16 +59,15 @@ describe RailsBrotliCache do
|
|
55
59
|
end
|
56
60
|
|
57
61
|
it "for #fetch" do
|
58
|
-
|
59
|
-
expect(brotli_store.fetch("val_key") {
|
60
|
-
expect(brotli_store.fetch("val_key", force: true) { val }).to eq(standard_cache.fetch("val_key", force: true) { val })
|
62
|
+
expect(brotli_store.fetch("val_key") { big_enough_to_compress_value }).to eq(standard_cache.fetch("val_key") { big_enough_to_compress_value })
|
63
|
+
expect(brotli_store.fetch("val_key", force: true) { big_enough_to_compress_value }).to eq(standard_cache.fetch("val_key", force: true) { big_enough_to_compress_value })
|
61
64
|
expect(brotli_store.fetch("val_key")).to eq(standard_cache.fetch("val_key"))
|
62
65
|
end
|
63
66
|
|
64
67
|
it "for #write_multi and #read_multi" do
|
65
68
|
values = {
|
66
69
|
"key_1" => "val_1",
|
67
|
-
"key_2" =>
|
70
|
+
"key_2" => big_enough_to_compress_value
|
68
71
|
}
|
69
72
|
|
70
73
|
brotli_store.write_multi(values)
|
@@ -78,7 +81,7 @@ describe RailsBrotliCache do
|
|
78
81
|
|
79
82
|
it "for #fetch_multi" do
|
80
83
|
values = {
|
81
|
-
"key_1" =>
|
84
|
+
"key_1" => big_enough_to_compress_value,
|
82
85
|
"key_2" => "val_2"
|
83
86
|
}
|
84
87
|
|
@@ -98,9 +98,22 @@ describe RailsBrotliCache do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
describe "fetch_multi" do
|
101
|
+
subject do
|
102
|
+
cache_store.fetch_multi(*keys) do |key|
|
103
|
+
big_enough_to_compress_value + key
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
let(:keys) { %w[key_1 key_2] }
|
108
|
+
let(:response) do
|
109
|
+
{
|
110
|
+
'key_1' => big_enough_to_compress_value + 'key_1',
|
111
|
+
'key_2' => big_enough_to_compress_value + 'key_2'
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
101
115
|
it "works" do
|
102
|
-
|
103
|
-
expect(cache_store.read("key_1")).to eq big_enough_to_compress_value
|
116
|
+
expect(subject).to eq response
|
104
117
|
end
|
105
118
|
end
|
106
119
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-brotli-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pawurb
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
|
-
type: :
|
34
|
+
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
@@ -226,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
226
|
- !ruby/object:Gem::Version
|
227
227
|
version: '0'
|
228
228
|
requirements: []
|
229
|
-
rubygems_version: 3.
|
229
|
+
rubygems_version: 3.3.7
|
230
230
|
signing_key:
|
231
231
|
specification_version: 4
|
232
232
|
summary: Drop-in enhancement for Rails cache, offering better performance and compression
|