rails-fast-cache 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1e10ff2fb43319e203157ada74f91675ba2c9e21c756afa561ca601dcbb4f36f
4
+ data.tar.gz: 3b765c36cf325775ffcaa52976bf9775df7a32cc3aac02c3b17d779ab793acad
5
+ SHA512:
6
+ metadata.gz: cf399f1cd89133979da4b1f41fe97c2f9ab014d1e63ee9e705f37eeaecd78a8cc0317108687585ecab36489817e468aeee85dce7f8e27037a9c8badaa9a83af1
7
+ data.tar.gz: 4bcfbd7e0de24062dd16dfdcced6ed61f4f3b192ee9d4d0574897d103405db6735a78857856b4e962fed0d985cf1ff14dac086572814af7be4de07347c3ef713
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2024 Filippo Liverani
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Rails Fast Cache
2
+
3
+ This gem provides a wrapper around Rails cache store that improves performance by:
4
+ - using Brotli as default compressor instead of GZip
5
+ - using MessagePack as default serializer instead of Marshal
6
+ - delegating cache writes to a thread pool instead of running them synchronously
7
+
8
+ ## Requirements
9
+ - Rails 7.1+
10
+ - You need to provide appropriate MessagePack serializers to cache custom classes
11
+
12
+ ## Installation
13
+
14
+ `Gemfile`
15
+
16
+ ```ruby
17
+ gem 'rails-fast-cache'
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ Rail Fast Cache implements ActionsSupport::Cache::Store API and can be
23
+ instantiated by passing the same parameters you would pass to Rails'
24
+ `config.cache_store` configuration option.
25
+
26
+ ```ruby
27
+ class Application < Rails::Application
28
+ ...
29
+ config.cache_store = RailsFastCache::Store.new(:memory_store, { size: 64.megabytes })
30
+ ```
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'brotli'
4
+
5
+ module RailsFastCache
6
+ module BrotliCompressor
7
+ COMPRESSION_PREFIX = "\x02".b
8
+ ZLIB_COMPRESSION_PREFIX = "\x78".b
9
+ COMPRESSION_QUALITY = 3
10
+
11
+ def self.deflate(dumped)
12
+ COMPRESSION_PREFIX +
13
+ ::Brotli.deflate(dumped, quality: COMPRESSION_QUALITY)
14
+ end
15
+
16
+ def self.inflate(compressed)
17
+ if compressed.start_with?(COMPRESSION_PREFIX)
18
+ ::Brotli.inflate(compressed.byteslice(1..-1))
19
+ elsif compressed.start_with?(ZLIB_COMPRESSION_PREFIX)
20
+ ::Zlib.inflate(compressed)
21
+ else
22
+ compressed
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_job'
4
+
5
+ module RailsFastCache
6
+ class NonSerializingJob < ::ActiveJob::Base
7
+ self.logger = nil
8
+
9
+ def deserialize_arguments(serialized_arguments)
10
+ serialized_arguments
11
+ end
12
+
13
+ def serialize_arguments(arguments)
14
+ arguments
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_job'
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
6
+
7
+ require_relative 'brotli_compressor'
8
+ require_relative 'write_job'
9
+ require_relative 'write_multi_job'
10
+
11
+ module RailsFastCache
12
+ class Store < ::ActiveSupport::Cache::Store
13
+ DEFAULT_EXECUTOR_OPTIONS = {
14
+ min_threads: ENV.fetch('RAILS_MAX_THREADS', 3).to_i,
15
+ max_threads: ENV.fetch('RAILS_MAX_THREADS', 3).to_i,
16
+ max_queue: 100,
17
+ fallback_policy: :caller_runs
18
+ }.freeze
19
+
20
+ delegate(
21
+ :cleanup,
22
+ :clear,
23
+ :decrement,
24
+ :delete,
25
+ :delete_matched,
26
+ :delete_multi,
27
+ :exist?,
28
+ :fetch,
29
+ :fetch_multi,
30
+ :increment,
31
+ :key_matcher,
32
+ :mute,
33
+ :new,
34
+ :read,
35
+ :read_multi,
36
+ :silence!,
37
+ to: :@store
38
+ )
39
+
40
+ def initialize(cache_store, *parameters)
41
+ options = parameters.extract_options!
42
+ options[:compressor] ||= RailsFastCache::BrotliCompressor if !options.key?(:coder) && cache_store != :memory_store
43
+ options[:serializer] ||= :message_pack unless options.key?(:coder)
44
+
45
+ @queue = ActiveJob::QueueAdapters::AsyncAdapter.new(
46
+ **DEFAULT_EXECUTOR_OPTIONS.merge(*options.delete(:executor))
47
+ )
48
+ @store = ActiveSupport::Cache.lookup_store(cache_store, *parameters, **options)
49
+ end
50
+
51
+ def write(name, value, options = nil)
52
+ @queue.enqueue(WriteJob.perform_later(@store, name, value, options))
53
+ end
54
+
55
+ def write_multi(hash, options = nil)
56
+ @queue.enqueue(WriteMultiJob.perform_later(@store, hash, options))
57
+ end
58
+
59
+ def shutdown(wait: true)
60
+ @queue.shutdown(wait:)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsFastCache
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'non_serializing_job'
4
+
5
+ module RailsFastCache
6
+ class WriteJob < NonSerializingJob
7
+ def perform(store, name, value, options)
8
+ store.write(name, value, options)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'non_serializing_job'
4
+
5
+ module RailsFastCache
6
+ class WriteMultiJob < NonSerializingJob
7
+ def perform(store, hash, options)
8
+ store.write_multi(hash, options)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rails-fast-cache/store'
4
+
5
+ module RailsFastCache; end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RailsFastCache::BrotliCompressor do
6
+ describe '#deflate' do
7
+ it 'compresses payload with Brotli' do
8
+ deflated = described_class.deflate('test')
9
+
10
+ expect(deflated).to start_with("\x02")
11
+ end
12
+ end
13
+
14
+ describe '#inflate' do
15
+ it 'decompresses payload with Brotli' do
16
+ inflated = described_class.inflate(described_class.deflate('test'))
17
+
18
+ expect(inflated).to eq('test')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe RailsFastCache::Store do
6
+ let(:value) do
7
+ {
8
+ time: Time.now,
9
+ string: SecureRandom.hex(2048)
10
+ }
11
+ end
12
+
13
+ def self.test_write(cache_store)
14
+ describe '#write' do
15
+ it "stores value in #{cache_store}" do
16
+ store = initialize_store(cache_store)
17
+
18
+ store.write('store_spec_key', value)
19
+ store.shutdown
20
+
21
+ expect(store.read('store_spec_key')).to eq(value)
22
+ end
23
+ end
24
+
25
+ describe '#write_multi' do
26
+ it "stores value in #{cache_store}" do
27
+ store = initialize_store(cache_store)
28
+ values = {
29
+ 'store_spec_key_1' => value,
30
+ 'store_spec_key_2' => value
31
+ }
32
+
33
+ store.write_multi(values)
34
+ store.shutdown
35
+
36
+ expect(store.read_multi('store_spec_key_1', 'store_spec_key_2')).to eq(values)
37
+ end
38
+ end
39
+ end
40
+
41
+ [
42
+ :memory_store,
43
+ [:file_store, 'tmp/cache/'],
44
+ :redis_cache_store
45
+ ].each do |cache_store|
46
+ test_write(cache_store)
47
+ end
48
+
49
+ def initialize_store(cache_store)
50
+ store = RailsFastCache::Store.new(*cache_store)
51
+ store.delete_matched('store_spec_*')
52
+ store
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require_relative '../lib/rails-fast-cache'
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-fast-cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Filippo Liverani
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-02-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activejob
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '7.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '7.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: brotli
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0.4'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: msgpack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: benchmark-ips
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: redis
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '5.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '5.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ description: Drop-in improvement for Rails cache
112
+ email:
113
+ - 1382917+filippoliverani@users.noreply.github.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - LICENSE.txt
119
+ - README.md
120
+ - lib/rails-fast-cache.rb
121
+ - lib/rails-fast-cache/brotli_compressor.rb
122
+ - lib/rails-fast-cache/non_serializing_job.rb
123
+ - lib/rails-fast-cache/store.rb
124
+ - lib/rails-fast-cache/version.rb
125
+ - lib/rails-fast-cache/write_job.rb
126
+ - lib/rails-fast-cache/write_multi_job.rb
127
+ - spec/rails-fast-cache/brotli_compressor_spec.rb
128
+ - spec/rails-fast-cache/store_spec.rb
129
+ - spec/spec_helper.rb
130
+ homepage: https://github.com/filippoliverani/rails-fast-cache
131
+ licenses:
132
+ - MIT
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 3.1.0
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubygems_version: 3.5.6
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Drop-in improvement for Rails cache, providing enhanced performance with
153
+ asynchronous processing and better default serialization and compression
154
+ test_files:
155
+ - spec/rails-fast-cache/brotli_compressor_spec.rb
156
+ - spec/rails-fast-cache/store_spec.rb
157
+ - spec/spec_helper.rb