rails-brotli-cache 0.6.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38153c74bf09ef2a13760b3dea18a3a3e97790104b1af8a8d244ee104d0ad34e
4
- data.tar.gz: ed7760dd1e7373fa7e855f57ec7082a185f68b114e43bd051d29fb4be7619020
3
+ metadata.gz: 6267cfdccacd5e402e500f1df681e16e8d0be371718e499d0abde30c64822dbd
4
+ data.tar.gz: 4648a2053526200a231c8cd36632715700a6ee197f672431ed2de1c150e036eb
5
5
  SHA512:
6
- metadata.gz: dc9430106e033158f55f6421d42253a8aaec9b62833687538a289010e8b67b1ffc4a75ba0b71071e5925b7256d2d070b5930719ebd40d5a60f28dc6d7cd4cd19
7
- data.tar.gz: 47483feabfac21321eb6e4e6a0df6d5ffa8646261dbeb7b728897f3026f4deff699e682a4cf525f8cca79b824082173637baf9517d8e96c3cfd91aaf9b0b3291
6
+ metadata.gz: 3dfdbc83794444e9be00962b4b5f6470f5c8803340cabbe3535f7143c87cc3bb21af6340d226e8b4b647cf6fd02a1318376daf98f4c50baa4707c70bcacca463
7
+ data.tar.gz: a8baf1bb1409642f3ea7e6251970a9fb8fbf1b3bbf6dade72a77f288d24de3f58fa2d00b36f24b4c82908a8f6e510564f9265b106297dcc1ddd7500b9c4f7306
data/README.md CHANGED
@@ -9,7 +9,7 @@ You can check out [this blog post](https://pawelurbanek.com/rails-brotli-cache)
9
9
  `Gemfile`
10
10
 
11
11
  ```ruby
12
- gem 'brotli' # brotli is an optional dependency because other compressors are supported
12
+ gem 'brotli' # an optional dependency, other compressors are supported
13
13
  gem 'rails-brotli-cache'
14
14
  ```
15
15
 
@@ -139,10 +139,21 @@ config.cache_store = RailsBrotliCache::Store.new(
139
139
  ```
140
140
 
141
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
+
142
153
  Rails.cache.write('test-key', json, compressor_class: Snappy)
143
154
  ```
144
155
 
145
- This config expects a class which defines two class methods `inflate` and `deflate`. It allows you to instead use for example a [Google Snappy algorithm](https://github.com/miyucy/snappy) offering even better performance for the cost of worse compresion ratios. Optionally, you can define a custom class wrapping any compression library.
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.
146
157
 
147
158
  ## Testing
148
159
 
data/benchmarks/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'brotli'
3
4
  gem 'rails-brotli-cache', path: "../"
4
5
  gem 'redis'
5
6
  gem 'dalli'
7
+ gem 'zstd-ruby'
data/benchmarks/main.rb CHANGED
@@ -1,31 +1,62 @@
1
1
  require 'active_support'
2
2
  require 'active_support/core_ext/hash'
3
3
  require 'net/http'
4
+ require 'brotli'
4
5
  require 'rails-brotli-cache'
5
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
6
18
 
7
19
  memory_cache = ActiveSupport::Cache::MemoryStore.new(compress: true) # memory store does not use compression by default
8
20
  brotli_memory_cache = RailsBrotliCache::Store.new(memory_cache)
21
+ zstd_memory_cache = RailsBrotliCache::Store.new(memory_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
22
+
9
23
  redis_cache = ActiveSupport::Cache::RedisCacheStore.new
10
24
  brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
25
+ zstd_redis_cache = RailsBrotliCache::Store.new(redis_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
26
+
11
27
  memcached_cache = ActiveSupport::Cache::MemCacheStore.new
12
28
  brotli_memcached_cache = RailsBrotliCache::Store.new(memcached_cache)
29
+ zstd_memcached_cache = RailsBrotliCache::Store.new(memcached_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
30
+
13
31
  file_cache = ActiveSupport::Cache::FileStore.new('/tmp')
14
32
  brotli_file_cache = RailsBrotliCache::Store.new(file_cache)
33
+ zstd_file_cache = RailsBrotliCache::Store.new(file_cache, compressor_class: ZSTDCompressor, prefix: "zs-")
15
34
 
16
35
  json_uri = URI("https://raw.githubusercontent.com/pawurb/rails-brotli-cache/main/spec/fixtures/sample.json")
17
36
  json = Net::HTTP.get(json_uri)
18
37
 
19
38
  puts "Uncompressed JSON size: #{json.size}"
20
39
  redis_cache.write("gz-json", json)
21
- gzip_json_size = redis_cache.redis.get("gz-json").size
40
+ gzip_json_size = redis_cache.redis.with do |conn|
41
+ conn.get("gz-json").size
42
+ end
22
43
  puts "Gzip JSON size: #{gzip_json_size}"
23
44
  brotli_redis_cache.write("json", json)
24
- br_json_size = redis_cache.redis.get("br-json").size
45
+ br_json_size = redis_cache.redis.with do |conn|
46
+ conn.get("br-json").size
47
+ end
25
48
  puts "Brotli JSON size: #{br_json_size}"
26
49
  puts "~#{((gzip_json_size - br_json_size).to_f / gzip_json_size.to_f * 100).round}% improvment"
27
50
  puts ""
28
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
+
29
60
  iterations = 100
30
61
 
31
62
  Benchmark.bm do |x|
@@ -43,6 +74,13 @@ Benchmark.bm do |x|
43
74
  end
44
75
  end
45
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
+
46
84
  x.report("redis_cache") do
47
85
  iterations.times do
48
86
  redis_cache.write("test", json)
@@ -57,6 +95,13 @@ Benchmark.bm do |x|
57
95
  end
58
96
  end
59
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
+
60
105
  x.report("memcached_cache") do
61
106
  iterations.times do
62
107
  memcached_cache.write("test", json)
@@ -71,6 +116,13 @@ Benchmark.bm do |x|
71
116
  end
72
117
  end
73
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
+
74
126
  x.report("file_cache") do
75
127
  iterations.times do
76
128
  file_cache.write("test", json)
@@ -84,4 +136,11 @@ Benchmark.bm do |x|
84
136
  brotli_file_cache.read("test")
85
137
  end
86
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
87
146
  end
@@ -109,14 +109,21 @@ module RailsBrotliCache
109
109
 
110
110
  def fetch_multi(*names)
111
111
  options = names.extract_options!
112
- names = names.map { |name| expanded_cache_key(name) }
112
+ expanded_names = names.map { |name| expanded_cache_key(name) }
113
113
  options = options.reverse_merge(@init_options)
114
114
 
115
- @core_store.fetch_multi(
116
- *names, options.merge(compress: false)
117
- ) do |name|
118
- compressed(yield(name), options)
115
+ reads = core_store.send(:read_multi_entries, expanded_names, **options)
116
+ reads.map do |key, val|
117
+ [source_cache_key(key), uncompressed(val, options)]
118
+ end.to_h
119
+
120
+ writes = {}
121
+ ordered = names.index_with do |name|
122
+ reads.fetch(name) { writes[name] = yield(name) }
119
123
  end
124
+
125
+ write_multi(writes)
126
+ ordered
120
127
  end
121
128
 
122
129
  def exist?(name, options = {})
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsBrotliCache
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.2"
5
5
  end
@@ -13,6 +13,15 @@ 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
+
20
+ after do
21
+ brotli_store.clear
22
+ standard_cache.clear
23
+ end
24
+
16
25
  describe "Brotli cache has the same API as #{cache_store_types[0].class}" do
17
26
  subject(:brotli_store) do
18
27
  RailsBrotliCache::Store.new(cache_store_types[0])
@@ -35,12 +44,12 @@ describe RailsBrotliCache do
35
44
  expect(brotli_store.write("int_val_key", int_val).class).to eq(standard_cache.write("int_val_key", int_val).class)
36
45
  expect(brotli_store.read("int_val_key")).to eq(standard_cache.read("int_val_key"))
37
46
 
38
- str_val = "str"
39
- expect(brotli_store.write("str_val_key", int_val).class).to eq(standard_cache.write("str_val_key", int_val).class)
47
+ str_val = big_enough_to_compress_value
48
+ expect(brotli_store.write("str_val_key", str_val).class).to eq(standard_cache.write("str_val_key", str_val).class)
40
49
  expect(brotli_store.read("str_val_key")).to eq(standard_cache.read("str_val_key"))
41
50
 
42
51
  complex_val = OpenStruct.new(a: 1, b: 2)
43
- expect(brotli_store.write("complex_val_key", int_val).class).to eq(standard_cache.write("complex_val_key", int_val).class)
52
+ expect(brotli_store.write("complex_val_key", complex_val).class).to eq(standard_cache.write("complex_val_key", complex_val).class)
44
53
  expect(brotli_store.read("complex_val_key")).to eq(standard_cache.read("complex_val_key"))
45
54
  end
46
55
 
@@ -55,15 +64,14 @@ describe RailsBrotliCache do
55
64
  end
56
65
 
57
66
  it "for #fetch" do
58
- val = "123"
59
- expect(brotli_store.fetch("val_key") { val }).to eq(standard_cache.fetch("val_key") { val })
60
- expect(brotli_store.fetch("val_key", force: true) { val }).to eq(standard_cache.fetch("val_key", force: true) { val })
67
+ expect(brotli_store.fetch("val_key") { big_enough_to_compress_value }).to eq(standard_cache.fetch("val_key") { big_enough_to_compress_value })
68
+ 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
69
  expect(brotli_store.fetch("val_key")).to eq(standard_cache.fetch("val_key"))
62
70
  end
63
71
 
64
72
  it "for #write_multi and #read_multi" do
65
73
  values = {
66
- "key_1" => "val_1",
74
+ "key_1" => big_enough_to_compress_value,
67
75
  "key_2" => "val_2"
68
76
  }
69
77
 
@@ -78,16 +86,16 @@ describe RailsBrotliCache do
78
86
 
79
87
  it "for #fetch_multi" do
80
88
  values = {
81
- "key_1" => "val_1",
89
+ "key_1" => big_enough_to_compress_value,
82
90
  "key_2" => "val_2"
83
91
  }
84
92
 
85
93
  brotli_store.fetch_multi("key_1", "key_2") do |key|
86
- "val_#{key.split('_').last}"
94
+ values[key]
87
95
  end
88
96
 
89
97
  standard_cache.fetch_multi("key_1", "key_2") do |key|
90
- "val_#{key.split('_').last}"
98
+ values[key]
91
99
  end
92
100
 
93
101
  expect(brotli_store.read("key_1")).to eq standard_cache.read("key_1")
@@ -98,9 +98,55 @@ describe RailsBrotliCache do
98
98
  end
99
99
 
100
100
  describe "fetch_multi" do
101
- it "works" do
102
- cache_store.fetch_multi("key_1", "key_2", expires_in: 5.seconds) { big_enough_to_compress_value }
103
- expect(cache_store.read("key_1")).to eq big_enough_to_compress_value
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
+
115
+ it "works for store and reread" do
116
+ expect(subject).to eq response
117
+
118
+ expect(cache_store.fetch_multi(*keys) do |key|
119
+ big_enough_to_compress_value + key
120
+ end).to eq response
121
+ end
122
+
123
+ context 'with a complex object that responds to #cache_key' do
124
+ subject do
125
+ cache_store.fetch_multi(*keys) do |key|
126
+ big_enough_to_compress_value + key.id
127
+ end
128
+ end
129
+
130
+ let(:keys) do
131
+ [
132
+ OpenStruct.new(cache_key: 'key_1', id: '12345'),
133
+ OpenStruct.new(cache_key: 'key_2', id: '54321')
134
+ ]
135
+ end
136
+ let(:response) do
137
+ {
138
+ keys[0] => big_enough_to_compress_value + '12345',
139
+ keys[1] => big_enough_to_compress_value + '54321'
140
+ }
141
+ end
142
+
143
+ it "works for store and reread" do
144
+ expect(subject).to eq response
145
+
146
+ expect(cache_store.fetch_multi(*keys) do |key|
147
+ big_enough_to_compress_value + key.id
148
+ end).to eq response
149
+ end
104
150
  end
105
151
  end
106
152
 
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.6.0
4
+ version: 0.6.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-10-09 00:00:00.000000000 Z
11
+ date: 2023-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -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.4.10
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