rails-brotli-cache 0.2.0 → 0.2.2
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 +40 -22
- data/benchmarks/Gemfile +3 -0
- data/benchmarks/main.rb +69 -0
- data/lib/rails-brotli-cache/store.rb +7 -5
- data/lib/rails-brotli-cache/version.rb +1 -1
- data/spec/rails-brotli-cache/rails_store_spec.rb +0 -8
- data/spec/rails-brotli-cache/redis_spec.rb +28 -1
- data/spec/rails-brotli-cache/store_spec.rb +6 -8
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed8813c2a9849358eb4d1756cd39eda4acf03846ea166880b956d9c630a5f3ee
|
4
|
+
data.tar.gz: cdff030d0d6bdd7e2b7790c5aea0441ecc89e184165c38e899dc5abdc7f5c900
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bf99730b2b31ed9591157fe75764fb15c17cf84cfd664d148a339e8552ed27dc62512cdff39f84552bc151aa4105d8720546eef38aa9e2da0552427d05ab86f
|
7
|
+
data.tar.gz: 7aa77827f595e73d55dc838ac8c6159a3f0f9138ed5bcdf57b2a0876060e01b66704dfd93f2eb8dc6502d109a0933f825bfc9d422858cbb786b03a75569f32d6
|
data/README.md
CHANGED
@@ -9,8 +9,8 @@ This gem enables support for compressing Ruby on Rails cache entries using the [
|
|
9
9
|
Brotli cache works as a proxy layer wrapping the underlying cache data store.
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
|
13
|
-
|
12
|
+
redis_cache = ActiveSupport::Cache::RedisCacheStore.new
|
13
|
+
brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
|
14
14
|
```
|
15
15
|
|
16
16
|
**~25%** better compression of a sample JSON object:
|
@@ -18,8 +18,8 @@ brotli_cache = RailsBrotliCache::Store.new(default_cache)
|
|
18
18
|
```ruby
|
19
19
|
json = File.read("sample.json") # sample 435kb JSON text
|
20
20
|
json.size # => 435662
|
21
|
-
|
22
|
-
|
21
|
+
redis_cache.write("json", json)
|
22
|
+
brotli_redis_cache.write("json", json)
|
23
23
|
|
24
24
|
## Check the size of cache entry stored in Redis
|
25
25
|
$redis.get("json").size # => 31698
|
@@ -30,8 +30,8 @@ $redis.get("br-json").size # => 24058
|
|
30
30
|
|
31
31
|
```ruby
|
32
32
|
users = User.limit(100).to_a # 100 ActiveRecord objects
|
33
|
-
|
34
|
-
|
33
|
+
redis_cache.write("users", users)
|
34
|
+
brotli_redis_cache.write("users", users)
|
35
35
|
$redis.get("users").size # => 12331
|
36
36
|
$redis.get("br-users").size # => 10299
|
37
37
|
```
|
@@ -39,27 +39,44 @@ $redis.get("br-users").size # => 10299
|
|
39
39
|
**~25%** faster performance for reading/writing a larger JSON file:
|
40
40
|
|
41
41
|
```ruby
|
42
|
-
json = File.read("sample.json") # sample
|
42
|
+
json = File.read("sample.json") # sample ~1mb JSON text
|
43
43
|
|
44
44
|
Benchmark.bm do |x|
|
45
|
-
x.report("
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
x.report("redis_cache") do
|
46
|
+
100.times do
|
47
|
+
redis_cache.write("test", json)
|
48
|
+
redis_cache.read("test")
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
x.report("
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
x.report("brotli_redis_cache") do
|
53
|
+
100.times do
|
54
|
+
brotli_redis_cache.write("test", json)
|
55
|
+
brotli_redis_cache.read("test")
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
# ...
|
58
60
|
end
|
59
61
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
62
|
+
# memory_cache 2.081221 0.051615 2.132836 ( 2.132877)
|
63
|
+
# brotli_memory_cache 1.134411 0.032996 1.167407 ( 1.167418)
|
64
|
+
# redis_cache 1.782225 0.049936 1.832161 ( 2.523317)
|
65
|
+
# brotli_redis_cache 1.218365 0.051084 1.269449 ( 1.850894)
|
66
|
+
# memcached_cache 1.766268 0.045351 1.811619 ( 2.504233)
|
67
|
+
# brotli_memcached_cache 1.194646 0.051750 1.246396 ( 1.752982)
|
68
|
+
```
|
69
|
+
|
70
|
+
Regardless of the underlying data store, Brotli cache offers between 20%-40% performance improvment.
|
71
|
+
|
72
|
+
You can run the benchmarks yourself by executing:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
cp docker-compose.yml.sample docker-compose.yml
|
76
|
+
docker compose up -d
|
77
|
+
cd benchmarks
|
78
|
+
bundle install
|
79
|
+
ruby main.rb
|
63
80
|
```
|
64
81
|
|
65
82
|
## Configuration
|
@@ -84,12 +101,13 @@ config.cache_store = RailsBrotliCache::Store.new(
|
|
84
101
|
)
|
85
102
|
```
|
86
103
|
|
87
|
-
Gem appends `br-` to the cache key names to prevent conflicts with previously saved cache entries. You can disable this behaviour by
|
88
|
-
|
89
|
-
`app/config/initializers/rails-brotli-cache.rb`
|
104
|
+
Gem appends `br-` to the cache key names to prevent conflicts with previously saved cache entries. You can disable this behaviour by passing `{ prefix: nil }` during initialization:
|
90
105
|
|
91
106
|
```ruby
|
92
|
-
|
107
|
+
config.cache_store = RailsBrotliCache::Store.new(
|
108
|
+
ActiveSupport::Cache::MemoryStore.new,
|
109
|
+
{ prefix: nil }
|
110
|
+
)
|
93
111
|
```
|
94
112
|
|
95
113
|
## Testing
|
data/benchmarks/Gemfile
ADDED
data/benchmarks/main.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'net/http'
|
3
|
+
require 'rails-brotli-cache'
|
4
|
+
|
5
|
+
memory_cache = ActiveSupport::Cache::MemoryStore.new(compress: true) # memory store does not use compression by default
|
6
|
+
brotli_memory_cache = RailsBrotliCache::Store.new(memory_cache)
|
7
|
+
redis_cache = ActiveSupport::Cache::RedisCacheStore.new
|
8
|
+
brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
|
9
|
+
memcached_cache = ActiveSupport::Cache::MemCacheStore.new
|
10
|
+
brotli_memcached_cache = RailsBrotliCache::Store.new(memcached_cache)
|
11
|
+
|
12
|
+
json_uri = URI("https://raw.githubusercontent.com/pawurb/rails-brotli-cache/main/spec/fixtures/sample.json")
|
13
|
+
json = Net::HTTP.get(json_uri)
|
14
|
+
|
15
|
+
puts "Uncompressed JSON size: #{json.size}"
|
16
|
+
redis_cache.write("gz-json", json)
|
17
|
+
gzip_json_size = redis_cache.redis.get("gz-json").size
|
18
|
+
puts "Gzip JSON size: #{gzip_json_size}"
|
19
|
+
brotli_redis_cache.write("json", json)
|
20
|
+
br_json_size = redis_cache.redis.get("br-json").size
|
21
|
+
puts "Brotli JSON size: #{br_json_size}"
|
22
|
+
puts "~#{((gzip_json_size - br_json_size).to_f / gzip_json_size.to_f * 100).round}% improvment"
|
23
|
+
puts ""
|
24
|
+
|
25
|
+
iterations = 100
|
26
|
+
|
27
|
+
Benchmark.bm do |x|
|
28
|
+
x.report("memory_cache") do
|
29
|
+
iterations.times do
|
30
|
+
memory_cache.write("test", json)
|
31
|
+
memory_cache.read("test")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
x.report("brotli_memory_cache") do
|
36
|
+
iterations.times do
|
37
|
+
brotli_memory_cache.write("test", json)
|
38
|
+
brotli_memory_cache.read("test")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
x.report("redis_cache") do
|
43
|
+
iterations.times do
|
44
|
+
redis_cache.write("test", json)
|
45
|
+
redis_cache.read("test")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
x.report("brotli_redis_cache") do
|
50
|
+
iterations.times do
|
51
|
+
brotli_redis_cache.write("test", json)
|
52
|
+
brotli_redis_cache.read("test")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
x.report("memcached_cache") do
|
57
|
+
iterations.times do
|
58
|
+
memcached_cache.write("test", json)
|
59
|
+
memcached_cache.read("test")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
x.report("brotli_memcached_cache") do
|
64
|
+
iterations.times do
|
65
|
+
brotli_memcached_cache.write("test", json)
|
66
|
+
brotli_memcached_cache.read("test")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -9,9 +9,15 @@ module RailsBrotliCache
|
|
9
9
|
COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY", 5).to_i
|
10
10
|
MARK_BR_COMPRESSED = "\x02".b
|
11
11
|
|
12
|
+
attr_reader :core_store
|
13
|
+
|
12
14
|
def initialize(core_store, options = {})
|
13
15
|
@core_store = core_store
|
14
|
-
@prefix =
|
16
|
+
@prefix = if options.key?(:prefix)
|
17
|
+
options.fetch(:prefix)
|
18
|
+
else
|
19
|
+
"br-"
|
20
|
+
end
|
15
21
|
end
|
16
22
|
|
17
23
|
def fetch(name, options = nil, &block)
|
@@ -74,10 +80,6 @@ module RailsBrotliCache
|
|
74
80
|
@core_store.clear
|
75
81
|
end
|
76
82
|
|
77
|
-
def disable_prefix!
|
78
|
-
@@prefix = nil
|
79
|
-
end
|
80
|
-
|
81
83
|
private
|
82
84
|
|
83
85
|
def cache_key(name)
|
@@ -73,12 +73,4 @@ describe RailsBrotliCache do
|
|
73
73
|
expect(cache_store.read("test-key")).to eq nil
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
77
|
-
describe "disable_prefix!" do
|
78
|
-
it "saves brotli cache entries without `br-` prefix" do
|
79
|
-
cache_store.disable_prefix!
|
80
|
-
cache_store.fetch("test-key") { 123 }
|
81
|
-
cache_store.instance_variable_set(:@prefix, "br-")
|
82
|
-
end
|
83
|
-
end
|
84
76
|
end
|
@@ -5,9 +5,14 @@ require 'spec_helper'
|
|
5
5
|
return unless ENV['RAILS_CACHE_STORE'] == 'redis_cache_store'
|
6
6
|
|
7
7
|
describe RailsBrotliCache do
|
8
|
+
let(:options) do
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
|
8
12
|
subject(:cache_store) do
|
9
13
|
RailsBrotliCache::Store.new(
|
10
|
-
ActiveSupport::Cache::RedisCacheStore.new(redis: $redis)
|
14
|
+
ActiveSupport::Cache::RedisCacheStore.new(redis: $redis),
|
15
|
+
options
|
11
16
|
)
|
12
17
|
end
|
13
18
|
|
@@ -27,4 +32,26 @@ describe RailsBrotliCache do
|
|
27
32
|
cache_store.write("test-key", json)
|
28
33
|
expect($redis.get("gz-test-key").size > $redis.get("br-test-key").size).to eq true
|
29
34
|
end
|
35
|
+
|
36
|
+
describe "disable_prefix" do
|
37
|
+
context "default prefix" do
|
38
|
+
it "appends 'br-' prefix" do
|
39
|
+
cache_store.fetch("test-key") { 123 }
|
40
|
+
expect($redis.get("test-key")).to eq nil
|
41
|
+
expect($redis.get("br-test-key")).to be_present
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "no prefix" do
|
46
|
+
let(:options) do
|
47
|
+
{ prefix: nil }
|
48
|
+
end
|
49
|
+
|
50
|
+
it "saves brotli cache entries without `br-` prefix" do
|
51
|
+
cache_store.fetch("test-key") { 123 }
|
52
|
+
expect($redis.get("br-test-key")).to eq nil
|
53
|
+
expect($redis.get("test-key")).to be_present
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
30
57
|
end
|
@@ -40,6 +40,12 @@ describe RailsBrotliCache do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
describe "#core_store" do
|
44
|
+
it "exposes the underlying data store" do
|
45
|
+
expect(cache_store.core_store.class).to eq ActiveSupport::Cache::MemoryStore
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
43
49
|
describe "#read and #write" do
|
44
50
|
it "reads values stored in Rails cache with a prefix" do
|
45
51
|
expect(cache_store.read("test-key")).to eq nil
|
@@ -67,12 +73,4 @@ describe RailsBrotliCache do
|
|
67
73
|
expect(cache_store.read("test-key")).to eq nil
|
68
74
|
end
|
69
75
|
end
|
70
|
-
|
71
|
-
describe "disable_prefix!" do
|
72
|
-
it "saves brotli cache entries without `br-` prefix" do
|
73
|
-
cache_store.disable_prefix!
|
74
|
-
cache_store.fetch("test-key") { 123 }
|
75
|
-
cache_store.instance_variable_set(:@prefix, "br-")
|
76
|
-
end
|
77
|
-
end
|
78
76
|
end
|
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.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pawurb
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -108,6 +108,8 @@ files:
|
|
108
108
|
- LICENSE.txt
|
109
109
|
- README.md
|
110
110
|
- Rakefile
|
111
|
+
- benchmarks/Gemfile
|
112
|
+
- benchmarks/main.rb
|
111
113
|
- docker-compose.yml.sample
|
112
114
|
- lib/rails-brotli-cache.rb
|
113
115
|
- lib/rails-brotli-cache/store.rb
|