rails-brotli-cache 0.3.14 → 0.4.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 +4 -4
- data/benchmarks/Gemfile +1 -1
- data/lib/rails-brotli-cache/store.rb +28 -31
- data/lib/rails-brotli-cache/version.rb +1 -1
- data/spec/rails-brotli-cache/redis_spec.rb +0 -16
- data/spec/rails-brotli-cache/store_spec.rb +88 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01a0f784d05c6228ab9f4305fddda0b0622a3b0a1dfaba079e952c051064a535
|
4
|
+
data.tar.gz: c12edd70df133be76fb09c2fdc179d83257307874d57f4f06fbe101fc639bc4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a7cc8c87363e93b947bc606f1c0811199ff47ac574fd0bf329ef6248e6305661d0f305874c950bc4c02ee86327b2029c6d22ab01be7de81ab1e94af54d111cd
|
7
|
+
data.tar.gz: 842e6e543337486dd313f65491ba9b56ca6fa15fcd8c94ed201d702d767d9ae41c777e240a6fd5791cbaa3d235f293a9d21281a76c8569271b7f1605b29377fe
|
data/benchmarks/Gemfile
CHANGED
@@ -6,9 +6,25 @@ 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
|
-
BR_COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY",
|
9
|
+
BR_COMPRESS_QUALITY = ENV.fetch("BR_CACHE_COMPRESS_QUALITY", 6).to_i
|
10
10
|
MARK_BR_COMPRESSED = "\x02".b
|
11
11
|
|
12
|
+
class BrotliCompressor
|
13
|
+
def self.deflate(payload)
|
14
|
+
::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.inflate(payload)
|
18
|
+
::Brotli.inflate(payload)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
DEFAULT_OPTIONS = {
|
23
|
+
compress_threshold: COMPRESS_THRESHOLD,
|
24
|
+
compress: true,
|
25
|
+
compressor_class: BrotliCompressor
|
26
|
+
}
|
27
|
+
|
12
28
|
attr_reader :core_store
|
13
29
|
|
14
30
|
def initialize(core_store, options = {})
|
@@ -19,11 +35,11 @@ module RailsBrotliCache
|
|
19
35
|
"br-"
|
20
36
|
end
|
21
37
|
|
22
|
-
@
|
38
|
+
@init_options = options.reverse_merge(DEFAULT_OPTIONS)
|
23
39
|
end
|
24
40
|
|
25
41
|
def fetch(name, options = nil, &block)
|
26
|
-
options
|
42
|
+
options = (options || {}).reverse_merge(@init_options)
|
27
43
|
|
28
44
|
if !block_given? && options[:force]
|
29
45
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -42,7 +58,7 @@ module RailsBrotliCache
|
|
42
58
|
end
|
43
59
|
|
44
60
|
def write(name, value, options = nil)
|
45
|
-
options = (options || {}).reverse_merge(
|
61
|
+
options = (options || {}).reverse_merge(@init_options)
|
46
62
|
payload = compressed(value, options)
|
47
63
|
|
48
64
|
@core_store.write(
|
@@ -53,6 +69,8 @@ module RailsBrotliCache
|
|
53
69
|
end
|
54
70
|
|
55
71
|
def read(name, options = nil)
|
72
|
+
options = (options || {}).reverse_merge(@init_options)
|
73
|
+
|
56
74
|
payload = @core_store.read(
|
57
75
|
cache_key(name),
|
58
76
|
options
|
@@ -62,7 +80,7 @@ module RailsBrotliCache
|
|
62
80
|
end
|
63
81
|
|
64
82
|
def write_multi(hash, options = nil)
|
65
|
-
options
|
83
|
+
options = (options || {}).reverse_merge(@init_options)
|
66
84
|
new_hash = hash.map do |key, val|
|
67
85
|
[
|
68
86
|
cache_key(key),
|
@@ -79,6 +97,7 @@ module RailsBrotliCache
|
|
79
97
|
def read_multi(*names)
|
80
98
|
options = names.extract_options!
|
81
99
|
names = names.map { |name| cache_key(name) }
|
100
|
+
options = options.reverse_merge(@init_options)
|
82
101
|
|
83
102
|
Hash[core_store.read_multi(*names, options).map do |key, val|
|
84
103
|
[source_cache_key(key), uncompressed(val, options)]
|
@@ -88,6 +107,7 @@ module RailsBrotliCache
|
|
88
107
|
def fetch_multi(*names)
|
89
108
|
options = names.extract_options!
|
90
109
|
names = names.map { |name| cache_key(name) }
|
110
|
+
options = options.reverse_merge(@init_options)
|
91
111
|
|
92
112
|
@core_store.fetch_multi(
|
93
113
|
*names, options.merge(compress: false)
|
@@ -125,13 +145,11 @@ module RailsBrotliCache
|
|
125
145
|
private
|
126
146
|
|
127
147
|
def compressed(value, options)
|
128
|
-
options ||= {}
|
129
|
-
|
130
148
|
return value if value.is_a?(Integer)
|
131
149
|
serialized = Marshal.dump(value)
|
132
150
|
|
133
|
-
if serialized.bytesize >=
|
134
|
-
compressor =
|
151
|
+
if serialized.bytesize >= options.fetch(:compress_threshold) && !options.fetch(:compress) == false
|
152
|
+
compressor = options.fetch(:compressor_class)
|
135
153
|
compressed_payload = compressor.deflate(serialized)
|
136
154
|
if compressed_payload.bytesize < serialized.bytesize
|
137
155
|
MARK_BR_COMPRESSED + compressed_payload
|
@@ -144,14 +162,12 @@ module RailsBrotliCache
|
|
144
162
|
end
|
145
163
|
|
146
164
|
def uncompressed(payload, options)
|
147
|
-
options ||= {}
|
148
|
-
|
149
165
|
return nil unless payload.present?
|
150
166
|
|
151
167
|
return payload if payload.is_a?(Integer)
|
152
168
|
|
153
169
|
serialized = if payload.start_with?(MARK_BR_COMPRESSED)
|
154
|
-
compressor =
|
170
|
+
compressor = options.fetch(:compressor_class)
|
155
171
|
compressor.inflate(payload.byteslice(1..-1))
|
156
172
|
else
|
157
173
|
payload
|
@@ -160,15 +176,6 @@ module RailsBrotliCache
|
|
160
176
|
Marshal.load(serialized)
|
161
177
|
end
|
162
178
|
|
163
|
-
def compressor_class(options, default:)
|
164
|
-
options = options || {}
|
165
|
-
if (klass = options[:compressor_class])
|
166
|
-
klass
|
167
|
-
else
|
168
|
-
default
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
179
|
def cache_key(name)
|
173
180
|
"#{@prefix}#{name}"
|
174
181
|
end
|
@@ -176,15 +183,5 @@ module RailsBrotliCache
|
|
176
183
|
def source_cache_key(name)
|
177
184
|
name.delete_prefix(@prefix.to_s)
|
178
185
|
end
|
179
|
-
|
180
|
-
class BrotliCompressor
|
181
|
-
def self.deflate(payload)
|
182
|
-
::Brotli.deflate(payload, quality: BR_COMPRESS_QUALITY)
|
183
|
-
end
|
184
|
-
|
185
|
-
def self.inflate(payload)
|
186
|
-
::Brotli.inflate(payload)
|
187
|
-
end
|
188
|
-
end
|
189
186
|
end
|
190
187
|
end
|
@@ -4,16 +4,6 @@ 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
|
-
|
17
7
|
describe RailsBrotliCache do
|
18
8
|
let(:options) do
|
19
9
|
{}
|
@@ -49,12 +39,6 @@ describe RailsBrotliCache do
|
|
49
39
|
expect($redis.get("gz-test-key").size < $redis.get("br-test-key").size).to eq true
|
50
40
|
end
|
51
41
|
|
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
|
-
|
58
42
|
describe "disable_prefix" do
|
59
43
|
context "default prefix" do
|
60
44
|
it "appends 'br-' prefix" do
|
@@ -4,7 +4,28 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe RailsBrotliCache do
|
6
6
|
subject(:cache_store) do
|
7
|
-
RailsBrotliCache::Store.new(
|
7
|
+
RailsBrotliCache::Store.new(
|
8
|
+
ActiveSupport::Cache::MemoryStore.new,
|
9
|
+
options
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:big_enough_to_compress_value) do
|
14
|
+
SecureRandom.hex(2048)
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:options) do
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
|
21
|
+
class DummyCompressor
|
22
|
+
def self.deflate(payload)
|
23
|
+
Zlib::Deflate.deflate(payload)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.inflate(payload)
|
27
|
+
Zlib::Inflate.inflate(payload)
|
28
|
+
end
|
8
29
|
end
|
9
30
|
|
10
31
|
describe "#fetch" do
|
@@ -40,12 +61,30 @@ describe RailsBrotliCache do
|
|
40
61
|
end
|
41
62
|
|
42
63
|
it "stores value in the configured Rails.cache when options passed" do
|
43
|
-
big_enough_to_compress_value = SecureRandom.hex(2048)
|
44
64
|
cache_store.fetch("test-key", expires_in: 5.seconds) { big_enough_to_compress_value }
|
45
65
|
expect(cache_store.read("test-key")).to eq big_enough_to_compress_value
|
46
66
|
end
|
47
67
|
end
|
48
68
|
|
69
|
+
describe "#write_multi and #read_multi" do
|
70
|
+
it "works" do
|
71
|
+
values = {
|
72
|
+
"key_1" => big_enough_to_compress_value,
|
73
|
+
"key_2" => 123
|
74
|
+
}
|
75
|
+
|
76
|
+
cache_store.write_multi(values, expires_in: 5.seconds)
|
77
|
+
expect(cache_store.read_multi("key_1", "key_2")).to eq values
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "fetch_multi" do
|
82
|
+
it "works" do
|
83
|
+
cache_store.fetch_multi("key_1", "key_2", expires_in: 5.seconds) { big_enough_to_compress_value }
|
84
|
+
expect(cache_store.read("key_1")).to eq big_enough_to_compress_value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
49
88
|
describe "#increment and #decrement" do
|
50
89
|
it "works" do
|
51
90
|
cache_store.write("integer-key", 0)
|
@@ -78,20 +117,60 @@ describe RailsBrotliCache do
|
|
78
117
|
end
|
79
118
|
|
80
119
|
describe "#read and #write" do
|
120
|
+
let(:one_kb_value) do
|
121
|
+
SecureRandom.hex(512)
|
122
|
+
end
|
123
|
+
|
81
124
|
it "reads values stored in Rails cache with a prefix" do
|
82
125
|
expect(cache_store.read("test-key")).to eq nil
|
83
|
-
expect(cache_store.write("test-key",
|
84
|
-
expect(cache_store.read("test-key")).to eq
|
126
|
+
expect(cache_store.write("test-key", big_enough_to_compress_value))
|
127
|
+
expect(cache_store.read("test-key")).to eq big_enough_to_compress_value
|
85
128
|
end
|
86
129
|
|
87
|
-
|
88
|
-
|
89
|
-
|
130
|
+
describe ":compressor_class option" do
|
131
|
+
context "as an init config" do
|
132
|
+
let(:options) do
|
133
|
+
{ compressor_class: DummyCompressor }
|
134
|
+
end
|
135
|
+
|
136
|
+
it "calls the custom compressor_class" do
|
137
|
+
expect(DummyCompressor).to receive(:deflate).and_call_original
|
138
|
+
cache_store.write("test-key", one_kb_value)
|
139
|
+
expect(DummyCompressor).to receive(:inflate).and_call_original
|
140
|
+
cache_store.read("test-key")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "as an method call" do
|
145
|
+
it "calls the custom compressor_class" do
|
146
|
+
expect(DummyCompressor).to receive(:deflate).and_call_original
|
147
|
+
cache_store.write("test-key", one_kb_value, compressor_class: DummyCompressor)
|
148
|
+
expect(DummyCompressor).to receive(:inflate).and_call_original
|
149
|
+
cache_store.read("test-key", compressor_class: DummyCompressor)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe ":compress_threshold option" do
|
155
|
+
it "applies compression for larger objects" do
|
156
|
+
expect(Brotli).to receive(:deflate).and_call_original
|
157
|
+
cache_store.write("test-key", one_kb_value)
|
90
158
|
end
|
91
159
|
|
92
|
-
it "does not apply compression" do
|
160
|
+
it "does not apply compression for smaller objects" do
|
161
|
+
expect(Brotli).not_to receive(:deflate)
|
93
162
|
cache_store.write("test-key", 123)
|
94
|
-
|
163
|
+
end
|
164
|
+
|
165
|
+
context "custom :compress_threshold value" do
|
166
|
+
let(:options) do
|
167
|
+
{ compress_threshold: 2.kilobyte }
|
168
|
+
end
|
169
|
+
|
170
|
+
it "does not apply compression for objects smaller then custom threshold" do
|
171
|
+
expect(Brotli).not_to receive(:deflate)
|
172
|
+
cache_store.write("test-key", one_kb_value)
|
173
|
+
end
|
95
174
|
end
|
96
175
|
end
|
97
176
|
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.
|
4
|
+
version: 0.4.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-05
|
11
|
+
date: 2023-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|