rails-brotli-cache 0.2.5 → 0.3.3

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: f25f2d703bace978a6a84e686d55a80aa926366d0795d032dc4bbab325b863af
4
- data.tar.gz: 027b2040bbe634ccc19e234bbbf550262add716e3f3ecdc2a5b1c82f947847c8
3
+ metadata.gz: 93b71dde73a988ed1a008810be38e9b30311896e41cf3c6017feebbfce4fec32
4
+ data.tar.gz: c3c9b8da01d4e3bcadd33fca01a3648b591db18dc807536190f43f34a4af2159
5
5
  SHA512:
6
- metadata.gz: b84b6bc932bed9537116349df16b39524fc4e93b7b9e54e49e254de080e7a0513fa4a0a58722cc5a1e4a455d0c5c7d9226cf9192731a5617f4bf772eb210c5aa
7
- data.tar.gz: bafd5345b64fb78db2815036eccbae16edd8fcbc7dc91b4acc8c1009e114be5c25e80b05e5498de1d500e3a10b138c8b086760127ba6462accaef33777fc57c8
6
+ metadata.gz: 33648f7e5a975d4c87ea10356e2ea5dc5220ade8d6a188ba3891a5387b47506079d4f4c7763f61458663868835e16f721489cd16132cd6ca671807531f14f0cc
7
+ data.tar.gz: 27d9f9b5c42134eccd351807bfd44f4d6fcc19baae2ef13a466f013c95406971feab785bced3c82827a709342f4a0672b7dee0e012f2f7c2003fac267daf7d43
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 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,17 +61,17 @@ 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
@@ -79,37 +83,56 @@ bundle exec ruby main.rb
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. 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/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)
@@ -44,7 +46,8 @@ module RailsBrotliCache
44
46
  options = (options || {}).reverse_merge(compress: true)
45
47
 
46
48
  payload = if serialized.bytesize >= COMPRESS_THRESHOLD && !options.fetch(:compress) == false
47
- compressed_payload = ::Brotli.deflate(serialized, quality: COMPRESS_QUALITY)
49
+ compressor = compressor_class(options, default: @compressor_class)
50
+ compressed_payload = compressor.deflate(serialized)
48
51
  if compressed_payload.bytesize < serialized.bytesize
49
52
  MARK_BR_COMPRESSED + compressed_payload
50
53
  else
@@ -70,7 +73,8 @@ module RailsBrotliCache
70
73
  return nil unless payload.present?
71
74
 
72
75
  serialized = if payload.start_with?(MARK_BR_COMPRESSED)
73
- ::Brotli.inflate(payload.byteslice(1..-1))
76
+ compressor = compressor_class(options, default: @compressor_class)
77
+ compressor.inflate(payload.byteslice(1..-1))
74
78
  else
75
79
  payload
76
80
  end
@@ -78,6 +82,10 @@ module RailsBrotliCache
78
82
  Marshal.load(serialized)
79
83
  end
80
84
 
85
+ def exist?(name, options = nil)
86
+ @core_store.exist?(cache_key(name), options)
87
+ end
88
+
81
89
  def delete(name, options = nil)
82
90
  @core_store.delete(cache_key(name), options)
83
91
  end
@@ -88,8 +96,27 @@ module RailsBrotliCache
88
96
 
89
97
  private
90
98
 
99
+ def compressor_class(options, default:)
100
+ options = options || {}
101
+ if (klass = options[:compressor_class])
102
+ klass
103
+ else
104
+ default
105
+ end
106
+ end
107
+
91
108
  def cache_key(name)
92
109
  "#{@prefix}#{name}"
93
110
  end
111
+
112
+ class BrotliCompressor
113
+ def self.deflate(payload)
114
+ ::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
115
+ end
116
+
117
+ def self.inflate(payload)
118
+ ::Brotli.inflate(payload)
119
+ end
120
+ end
94
121
  end
95
122
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsBrotliCache
4
- VERSION = "0.2.5"
4
+ VERSION = "0.3.3"
5
5
  end
@@ -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
  {}
@@ -39,6 +49,12 @@ describe RailsBrotliCache do
39
49
  expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
40
50
  end
41
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
+
42
58
  describe "disable_prefix" do
43
59
  context "default prefix" do
44
60
  it "appends 'br-' prefix" do
@@ -40,6 +40,17 @@ describe RailsBrotliCache do
40
40
  end
41
41
  end
42
42
 
43
+ describe "exist?" do
44
+ it "returns true if cache entry exists" do
45
+ cache_store.write("test-key", 1234)
46
+ expect(cache_store.exist?("test-key")).to eq true
47
+ end
48
+
49
+ it "returns false if cache entry does not exist" do
50
+ expect(cache_store.exist?("test-key")).to eq false
51
+ end
52
+ end
53
+
43
54
  describe "#core_store" do
44
55
  it "exposes the underlying data store" do
45
56
  expect(cache_store.core_store.class).to eq ActiveSupport::Cache::MemoryStore
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.5
4
+ version: 0.3.3
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-21 00:00:00.000000000 Z
11
+ date: 2023-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails