rails-brotli-cache 0.2.3 → 0.3.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: 8f5eae03f927b9f70606cc7b4134fc7173926951da4cf05172bdd9cecb149c61
4
- data.tar.gz: c89a69ec5b5d5ddbb8efb432d253784ec30be073ca0a4d142edf347cbc4b75cb
3
+ metadata.gz: c53c91a3df8a561eadcb309bc1a0a3315ca331ec36fe9bfe0995b21dc09c821b
4
+ data.tar.gz: d74f60c63d75643f57ac1c3568b436ed817dcb69e73cd19d4ab369c420deeba2
5
5
  SHA512:
6
- metadata.gz: 7b8b3daea70b8c8f5cd8f445ee08155eb6356fbab042db3fd505dc26e666fe8a1011080391940d1d77e08d912ddef46d9a7908377bf6135c7784c433a37ce495
7
- data.tar.gz: 5c37c70c355770b708b7703333a4f941d5b28e51d8a6e140eb256ce5a145fa8da21a86cd72af9498f274c56c97d5b3daa9718e8a58aefe4e206d31f58a8d1471
6
+ metadata.gz: f2f4b9f01712a860dd57b6da48cb284c68a92593a50955796abf8eafd8b32e5b712233d0e7029aa3c26ed65b098547e25a3311b83cf6c94f8c6f74b2cdcaf443
7
+ data.tar.gz: c858ee0fd911aa7805ed2b74ab9dc15328b3584918b5da262b60df3a8a27f9982412b00001ba9da4f189038c57e9b0e2620116dba60b4982b91b5da8f7ad8ae4
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
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)
2
2
 
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 faster performance compared to the default `Rails.cache` regardless of the underlying data store.
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 faster performance compared to the default `Rails.cache`, regardless of the underlying data store. The gem also allows specifying any custom compression algorithm instead of Brotli.
4
4
 
5
5
  ## Benchmarks
6
6
 
7
7
  Brotli cache works as a proxy layer wrapping the underlying cache data store.
8
8
 
9
9
  ```ruby
10
- redis_cache = ActiveSupport::Cache::RedisCacheStore.new
10
+ redis_cache = ActiveSupport::Cache::RedisCacheStore.new(
11
+ url: "redis://localhost:6379"
12
+ )
11
13
  brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
12
14
  ```
13
15
 
@@ -19,9 +21,10 @@ json.size # => 435662
19
21
  redis_cache.write("json", json)
20
22
  brotli_redis_cache.write("json", json)
21
23
 
22
- ## Check the size of cache entry stored in Redis
23
- $redis.get("json").size # => 31698
24
- $redis.get("br-json").size # => 24058
24
+ ## Check the size of cache entries stored in Redis
25
+ redis = Redis.new(url: "redis://localhost:6379")
26
+ redis.get("json").size # => 31698
27
+ redis.get("br-json").size # => 24058
25
28
  ```
26
29
 
27
30
  **~20%** better compression of a sample ActiveRecord objects array:
@@ -30,8 +33,9 @@ $redis.get("br-json").size # => 24058
30
33
  users = User.limit(100).to_a # 100 ActiveRecord objects
31
34
  redis_cache.write("users", users)
32
35
  brotli_redis_cache.write("users", users)
33
- $redis.get("users").size # => 12331
34
- $redis.get("br-users").size # => 10299
36
+
37
+ redis.get("users").size # => 12331
38
+ redis.get("br-users").size # => 10299
35
39
  ```
36
40
 
37
41
  **~25%** faster performance for reading/writing a larger JSON file:
@@ -57,59 +61,78 @@ Benchmark.bm do |x|
57
61
  # ...
58
62
  end
59
63
 
60
- # memory_cache 2.081221 0.051615 2.132836 ( 2.132877)
61
- # brotli_memory_cache 1.134411 0.032996 1.167407 ( 1.167418)
62
64
  # redis_cache 1.782225 0.049936 1.832161 ( 2.523317)
63
65
  # brotli_redis_cache 1.218365 0.051084 1.269449 ( 1.850894)
64
66
  # memcached_cache 1.766268 0.045351 1.811619 ( 2.504233)
65
67
  # brotli_memcached_cache 1.194646 0.051750 1.246396 ( 1.752982)
68
+ # file_cache 1.727967 0.071138 1.799105 ( 1.799229)
69
+ # brotli_file_cache 1.128514 0.044308 1.172822 ( 1.172983)
66
70
  ```
67
71
 
68
- Regardless of the underlying data store, Brotli cache offers between 20%-40% performance improvment.
72
+ Regardless of the underlying data store, Brotli cache offers 20%-40% performance improvement.
69
73
 
70
- You can run the benchmarks yourself by executing:
74
+ You can run the benchmarks by executing:
71
75
 
72
76
  ```ruby
73
77
  cp docker-compose.yml.sample docker-compose.yml
74
78
  docker compose up -d
75
79
  cd benchmarks
76
80
  bundle install
77
- ruby main.rb
81
+ bundle exec ruby main.rb
78
82
  ```
79
83
 
80
84
  ## Configuration
81
85
 
82
- Gem works as a drop-in replacement for a standard Rails cache store. Here's how you can configure it with different store types:
86
+ Gem works as a drop-in replacement for a standard Rails cache store. You can configure it with different store types:
83
87
 
84
88
  ```ruby
85
89
  config.cache_store = RailsBrotliCache::Store.new(
86
- ActiveSupport::Cache::RedisCacheStore.new(redis: $redis)
90
+ ActiveSupport::Cache::RedisCacheStore.new(url: "redis://localhost:6379")
87
91
  )
88
92
  ```
89
93
 
90
94
  ```ruby
91
95
  config.cache_store = RailsBrotliCache::Store.new(
92
- ActiveSupport::Cache::MemoryStore.new
96
+ ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
93
97
  )
94
98
  ```
95
99
 
96
100
  ```ruby
97
101
  config.cache_store = RailsBrotliCache::Store.new(
98
- ActiveSupport::Cache::MemCacheStore.new("localhost:11211")
102
+ ActiveSupport::Cache::FileStore.new('/tmp')
99
103
  )
100
104
  ```
101
105
 
102
- 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:
106
+ You should avoid using it with `ActiveSupport::Cache::MemoryStore`. This type of cache store does not serialize or compress objects but keeps them directly in the RAM of a Ruby process. In this case, adding this gem would reduce RAM usage but add huge performance overhead.
107
+
108
+ Gem appends `br-` to the cache key names to prevent conflicts with previously saved entries. You can disable this behavior by passing `{ prefix: nil }` during initialization:
103
109
 
104
110
  ```ruby
105
111
  config.cache_store = RailsBrotliCache::Store.new(
106
- ActiveSupport::Cache::MemoryStore.new,
112
+ ActiveSupport::Cache::RedisCacheStore.new,
107
113
  { prefix: nil }
108
114
  )
109
115
  ```
110
116
 
111
117
  Addition of the prefix means that you can safely add the Brotli the cache config and avoid compression algorithm conflicts between old and new entries. After configuring the Brotli cache you should run `Rails.cache.clear` to remove the outdated (gzipped) entries.
112
118
 
119
+ ### Use a custom compressor class
120
+
121
+ By default gem uses a Brotli compression, but you can customize the algorithm. You can pass a `compressor_class` object as a store configuration argument or directly to `read/write/fetch` methods:
122
+
123
+ ```ruby
124
+ config.cache_store = RailsBrotliCache::Store.new(
125
+ ActiveSupport::Cache::RedisCacheStore.new,
126
+ { compressor_class: Snappy }
127
+ )
128
+ ```
129
+
130
+ ```ruby
131
+ Rails.cache.write('test-key', json, compressor_class: Snappy)
132
+ ```
133
+
134
+ 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.
135
+
113
136
  ## Testing
114
137
 
115
138
  ```bash
data/benchmarks/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'rails-brotli-cache'
4
+ gem 'redis'
5
+ gem 'dalli'
data/benchmarks/main.rb CHANGED
@@ -8,6 +8,8 @@ redis_cache = ActiveSupport::Cache::RedisCacheStore.new
8
8
  brotli_redis_cache = RailsBrotliCache::Store.new(redis_cache)
9
9
  memcached_cache = ActiveSupport::Cache::MemCacheStore.new
10
10
  brotli_memcached_cache = RailsBrotliCache::Store.new(memcached_cache)
11
+ file_cache = ActiveSupport::Cache::FileStore.new('/tmp')
12
+ brotli_file_cache = RailsBrotliCache::Store.new(file_cache)
11
13
 
12
14
  json_uri = URI("https://raw.githubusercontent.com/pawurb/rails-brotli-cache/main/spec/fixtures/sample.json")
13
15
  json = Net::HTTP.get(json_uri)
@@ -66,4 +68,18 @@ Benchmark.bm do |x|
66
68
  brotli_memcached_cache.read("test")
67
69
  end
68
70
  end
71
+
72
+ x.report("file_cache") do
73
+ iterations.times do
74
+ file_cache.write("test", json)
75
+ file_cache.read("test")
76
+ end
77
+ end
78
+
79
+ x.report("brotli_file_cache") do
80
+ iterations.times do
81
+ brotli_file_cache.write("test", json)
82
+ brotli_file_cache.read("test")
83
+ end
84
+ end
69
85
  end
@@ -6,7 +6,7 @@ require 'brotli'
6
6
  module RailsBrotliCache
7
7
  class Store < ::ActiveSupport::Cache::Store
8
8
  COMPRESS_THRESHOLD = ENV.fetch("BR_CACHE_COMPRESS_THRESHOLD", 1).to_f * 1024.0
9
- COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY", 5).to_i
9
+ BR_COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY", 5).to_i
10
10
  MARK_BR_COMPRESSED = "\x02".b
11
11
 
12
12
  attr_reader :core_store
@@ -18,6 +18,8 @@ module RailsBrotliCache
18
18
  else
19
19
  "br-"
20
20
  end
21
+
22
+ @compressor_class = compressor_class(options, default: BrotliCompressor)
21
23
  end
22
24
 
23
25
  def fetch(name, options = nil, &block)
@@ -41,9 +43,11 @@ module RailsBrotliCache
41
43
 
42
44
  def write(name, value, options = nil)
43
45
  serialized = Marshal.dump(value)
46
+ options = (options || {}).reverse_merge(compress: true)
44
47
 
45
- payload = if serialized.bytesize >= COMPRESS_THRESHOLD
46
- compressed_payload = ::Brotli.deflate(serialized, quality: COMPRESS_QUALITY)
48
+ payload = if serialized.bytesize >= COMPRESS_THRESHOLD && !options.fetch(:compress) == false
49
+ compressor = compressor_class(options, default: @compressor_class)
50
+ compressed_payload = compressor.deflate(serialized)
47
51
  if compressed_payload.bytesize < serialized.bytesize
48
52
  MARK_BR_COMPRESSED + compressed_payload
49
53
  else
@@ -56,7 +60,7 @@ module RailsBrotliCache
56
60
  @core_store.write(
57
61
  cache_key(name),
58
62
  payload,
59
- (options || {}).merge(compress: false)
63
+ options.merge(compress: false)
60
64
  )
61
65
  end
62
66
 
@@ -69,7 +73,8 @@ module RailsBrotliCache
69
73
  return nil unless payload.present?
70
74
 
71
75
  serialized = if payload.start_with?(MARK_BR_COMPRESSED)
72
- ::Brotli.inflate(payload.byteslice(1..-1))
76
+ compressor = compressor_class(options, default: @compressor_class)
77
+ compressor.inflate(payload.byteslice(1..-1))
73
78
  else
74
79
  payload
75
80
  end
@@ -87,8 +92,27 @@ module RailsBrotliCache
87
92
 
88
93
  private
89
94
 
95
+ def compressor_class(options, default:)
96
+ options = options || {}
97
+ if (klass = options[:compressor_class])
98
+ klass
99
+ else
100
+ default
101
+ end
102
+ end
103
+
90
104
  def cache_key(name)
91
105
  "#{@prefix}#{name}"
92
106
  end
107
+
108
+ class BrotliCompressor
109
+ def self.deflate(payload)
110
+ ::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
111
+ end
112
+
113
+ def self.inflate(payload)
114
+ ::Brotli.inflate(payload)
115
+ end
116
+ end
93
117
  end
94
118
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsBrotliCache
4
- VERSION = "0.2.3"
4
+ VERSION = "0.3.2"
5
5
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = RailsBrotliCache::VERSION
9
9
  gem.authors = ["pawurb"]
10
10
  gem.email = ["contact@pawelurbanek.com"]
11
- gem.summary = %q{ Rails cache using Brotli algorithm offers better compression and performance. }
12
- gem.description = %q{ rails-brotli-cache allows to reduce storage needed for Rails cache by using Brotli compression which can produce outputs smaller by ~20%. }
11
+ gem.summary = %q{ Drop-in enhancement for Rails cache, offering better performance and compression with Brotli algorithm. }
12
+ gem.description = %q{ This gem reduces storage needed for Rails cache by using Brotli compression, which can produce outputs smaller by ~20% and offers better performance than Gzip. }
13
13
  gem.homepage = "https://github.com/pawurb/rails-brotli-cache"
14
14
  gem.files = `git ls-files`.split("\n")
15
15
  gem.test_files = gem.files.grep(%r{^(spec)/})
@@ -4,6 +4,16 @@ require 'spec_helper'
4
4
 
5
5
  return unless ENV['RAILS_CACHE_STORE'] == 'redis_cache_store'
6
6
 
7
+ class DummyCompressor
8
+ def self.deflate(payload)
9
+ payload
10
+ end
11
+
12
+ def self.inflate(payload)
13
+ payload
14
+ end
15
+ end
16
+
7
17
  describe RailsBrotliCache do
8
18
  let(:options) do
9
19
  {}
@@ -33,6 +43,18 @@ describe RailsBrotliCache do
33
43
  expect($redis.get("gz-test-key").size > $redis.get("br-test-key").size).to eq true
34
44
  end
35
45
 
46
+ it "respects { compress: false } setting and does not apply compression" do
47
+ Rails.cache.write("gz-test-key", json)
48
+ cache_store.write("test-key", json, compress: false)
49
+ expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
50
+ end
51
+
52
+ it "allows specyfing custom compressor class" do
53
+ Rails.cache.write("gz-test-key", json)
54
+ cache_store.write("test-key", json, compressor_class: DummyCompressor)
55
+ expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
56
+ end
57
+
36
58
  describe "disable_prefix" do
37
59
  context "default prefix" do
38
60
  it "appends 'br-' prefix" do
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.3
4
+ version: 0.3.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-20 00:00:00.000000000 Z
11
+ date: 2023-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -94,8 +94,8 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: " rails-brotli-cache allows to reduce storage needed for Rails cache
98
- by using Brotli compression which can produce outputs smaller by ~20%. "
97
+ description: " This gem reduces storage needed for Rails cache by using Brotli compression,
98
+ which can produce outputs smaller by ~20% and offers better performance than Gzip. "
99
99
  email:
100
100
  - contact@pawelurbanek.com
101
101
  executables: []
@@ -184,7 +184,8 @@ requirements: []
184
184
  rubygems_version: 3.1.6
185
185
  signing_key:
186
186
  specification_version: 4
187
- summary: Rails cache using Brotli algorithm offers better compression and performance.
187
+ summary: Drop-in enhancement for Rails cache, offering better performance and compression
188
+ with Brotli algorithm.
188
189
  test_files:
189
190
  - spec/dummy/Gemfile
190
191
  - spec/dummy/README.md