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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5fcc9aa5854debba71fab6f5df69615ab2b9edaa6620ae139e7c68c598c0dcb
4
- data.tar.gz: a60ba10b497f50e751ca191ce8e9034bd45b30c93e3dff718a26c14e178ffed9
3
+ metadata.gz: e109b9b356474fb8085b1cceaa892695de997465db4c90872556f76419da8f86
4
+ data.tar.gz: c8422310e9fed160052637a40dc7b1150b938c0f891b3b7b011eacdd11b8e4bb
5
5
  SHA512:
6
- metadata.gz: 066d33ad8b2e12d2fa4242ac120cbd0cd1ceee693619146d72b2dfe95ecd8dfe69b5b01028488a31e5373c5472602c42219cb2e0e749d9a2661c0cbcc26b9cb1
7
- data.tar.gz: a8522dff7f3452c216803cf5a45c62eb0ba1acadc3d4efece1130c35574344d4b2509938a076f78293814c271376b576973135791fc5fdf342240d587a2783cc
6
+ metadata.gz: 6b435443fbf3a6a09baed87b124429e43dcd4d3fbd06cab175a15818b397285aeab96ad0694dc8a2f0e0d91c3478400ba0e3081045e93ca63e275e62626f0e0d
7
+ data.tar.gz: 95af20d56721fbe1e9118b29a0fa1f7dc3fe5825d8c7ee04d77b783b96b405d6b1e765f4d8b63e8668d9414ca1e716cc010e3201d7a93340d35251ebd966f9e8
data/README.md CHANGED
@@ -1,8 +1,17 @@
1
- # Rails Brotli Cache [![Gem Version](https://img.shields.io/gem/v/rails-brotli-cache)](https://badge.fury.io/rb/rails-brotli-cache) [![CircleCI](https://circleci.com/gh/pawurb/rails-brotli-cache.svg?style=svg)](https://circleci.com/gh/pawurb/rails-brotli-cache)
1
+ # Rails Brotli Cache [![Gem Version](https://img.shields.io/gem/v/rails-brotli-cache)](https://badge.fury.io/rb/rails-brotli-cache) [![GH Actions](https://github.com/pawurb/rails-brotli-cache/actions/workflows/ci.yml/badge.svg)](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
- I'm currently working on a post describing the gem in more detail. You can subscribe to [my blog's mailing list](https://eepurl.com/dhuFg5) or [follow me on Twitter](https://twitter.com/_pawurb) to get notified when it's out.
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 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.
137
157
 
138
158
  ## Testing
139
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,30 +1,62 @@
1
- require "active_support"
2
- require "active_support/core_ext/hash"
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.get("gz-json").size
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.get("br-json").size
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
- require 'brotli'
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 = {})
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsBrotliCache
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.1"
5
5
  end
@@ -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.add_dependency "brotli"
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", int_val).class).to eq(standard_cache.write("int_val_key", int_val).class)
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
- 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 })
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" => "val_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" => "val_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
- 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
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.5.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-08-19 00:00:00.000000000 Z
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: :runtime
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.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