redis-memo 0.1.0 → 1.0.0
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/lib/redis_memo.rb +48 -36
- data/lib/redis_memo/after_commit.rb +2 -2
- data/lib/redis_memo/batch.rb +36 -11
- data/lib/redis_memo/cache.rb +36 -19
- data/lib/redis_memo/connection_pool.rb +4 -3
- data/lib/redis_memo/errors.rb +9 -0
- data/lib/redis_memo/future.rb +22 -13
- data/lib/redis_memo/memoizable.rb +109 -72
- data/lib/redis_memo/memoizable/bump_version.lua +39 -0
- data/lib/redis_memo/memoizable/dependency.rb +10 -10
- data/lib/redis_memo/memoizable/invalidation.rb +68 -66
- data/lib/redis_memo/memoize_method.rb +169 -131
- data/lib/redis_memo/memoize_query.rb +135 -92
- data/lib/redis_memo/memoize_query/cached_select.rb +59 -44
- data/lib/redis_memo/memoize_query/cached_select/connection_adapter.rb +7 -7
- data/lib/redis_memo/memoize_query/invalidation.rb +24 -20
- data/lib/redis_memo/memoize_query/memoize_table_column.rb +1 -0
- data/lib/redis_memo/middleware.rb +3 -1
- data/lib/redis_memo/options.rb +106 -5
- data/lib/redis_memo/railtie.rb +11 -0
- data/lib/redis_memo/redis.rb +15 -1
- data/lib/redis_memo/testing.rb +49 -0
- data/lib/redis_memo/thread_local_var.rb +16 -0
- data/lib/redis_memo/tracer.rb +1 -0
- data/lib/redis_memo/util.rb +19 -0
- metadata +80 -4
@@ -9,7 +9,9 @@ class RedisMemo::Middleware
|
|
9
9
|
result = nil
|
10
10
|
|
11
11
|
RedisMemo::Cache.with_local_cache do
|
12
|
-
|
12
|
+
RedisMemo.with_max_connection_attempts(RedisMemo::DefaultOptions.max_connection_attempts) do
|
13
|
+
result = @app.call(env)
|
14
|
+
end
|
13
15
|
end
|
14
16
|
RedisMemo::Memoizable::Invalidation.drain_invalidation_queue
|
15
17
|
|
data/lib/redis_memo/options.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
##
|
4
|
+
# This class allows users to configure various RedisMemo options. Options can be set in
|
5
|
+
# your initializer +config/initializers/redis_memo.rb+
|
6
|
+
# RedisMemo.configure do |config|
|
7
|
+
# config.expires_in = 3.hours
|
8
|
+
# config.global_cache_key_version = SecureRandom.uuid
|
9
|
+
# end
|
10
|
+
#
|
3
11
|
class RedisMemo::Options
|
4
12
|
def initialize(
|
5
13
|
async: nil,
|
@@ -9,33 +17,68 @@ class RedisMemo::Options
|
|
9
17
|
redis_error_handler: nil,
|
10
18
|
tracer: nil,
|
11
19
|
global_cache_key_version: nil,
|
12
|
-
expires_in: nil
|
20
|
+
expires_in: nil,
|
21
|
+
max_connection_attempts: nil,
|
22
|
+
disable_all: false,
|
23
|
+
disable_cached_select: false,
|
24
|
+
disabled_models: Set.new
|
13
25
|
)
|
26
|
+
@async = async
|
14
27
|
@compress = compress.nil? ? true : compress
|
15
28
|
@compress_threshold = compress_threshold || 1.kilobyte
|
16
29
|
@redis_config = redis
|
17
|
-
@
|
30
|
+
@redis = nil
|
18
31
|
@redis_error_handler = redis_error_handler
|
19
32
|
@tracer = tracer
|
20
33
|
@logger = logger
|
21
34
|
@global_cache_key_version = global_cache_key_version
|
22
35
|
@expires_in = expires_in
|
36
|
+
@max_connection_attempts = ENV['REDIS_MEMO_MAX_ATTEMPTS_PER_REQUEST']&.to_i || max_connection_attempts
|
37
|
+
@disable_all = ENV['REDIS_MEMO_DISABLE_ALL'] == 'true' || disable_all
|
38
|
+
@disable_cached_select = ENV['REDIS_MEMO_DISABLE_CACHED_SELECT'] == 'true' || disable_cached_select
|
39
|
+
@disabled_models = disabled_models
|
23
40
|
end
|
24
41
|
|
42
|
+
# Retrieves the redis client, initializing it if it does not exist yet.
|
25
43
|
def redis
|
26
|
-
@
|
44
|
+
@redis ||= RedisMemo::Redis.new(redis_config)
|
27
45
|
end
|
28
46
|
|
47
|
+
# Retrieves the config values used to initialize the Redis client.
|
29
48
|
def redis_config
|
30
49
|
@redis_config || {}
|
31
50
|
end
|
32
51
|
|
52
|
+
# Set configuration values to pass to the Redis client. If multiple configurations are passed
|
53
|
+
# to this method, we assume that the first config corresponds to the primary node, and subsequent
|
54
|
+
# configurations correspond to replica nodes.
|
55
|
+
#
|
56
|
+
# For example, if your urls are specified as <tt><url>,<url>...;<url>,...;...,</tt> where <tt>;</tt> delimits
|
57
|
+
# different clusters and <tt>,</tt> delimits primary and read replicas, then in your configuration:
|
58
|
+
#
|
59
|
+
# RedisMemo.configure do |config|
|
60
|
+
# config.redis = redis_urls.split(';').map do |urls|
|
61
|
+
# urls.split(',').map do |url|
|
62
|
+
# {
|
63
|
+
# url: url,
|
64
|
+
# # All timeout values are specified in seconds
|
65
|
+
# connect_timeout: ENV['REDIS_MEMO_CONNECT_TIMEOUT']&.to_f || 0.2,
|
66
|
+
# read_timeout: ENV['REDIS_MEMO_READ_TIMEOUT']&.to_f || 0.5,
|
67
|
+
# write_timeout: ENV['REDIS_MEMO_WRITE_TIMEOUT']&.to_f || 0.5,
|
68
|
+
# reconnect_attempts: ENV['REDIS_MEMO_RECONNECT_ATTEMPTS']&.to_i || 0
|
69
|
+
# }
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
33
74
|
def redis=(config)
|
34
75
|
@redis_config = config
|
35
|
-
@
|
76
|
+
@redis = nil
|
36
77
|
redis
|
37
78
|
end
|
38
79
|
|
80
|
+
# Sets the tracer object. Allows the tracer to be dynamically determined at
|
81
|
+
# runtime if a blk is given.
|
39
82
|
def tracer(&blk)
|
40
83
|
if blk.nil?
|
41
84
|
return @tracer if @tracer.respond_to?(:trace)
|
@@ -46,6 +89,8 @@ class RedisMemo::Options
|
|
46
89
|
end
|
47
90
|
end
|
48
91
|
|
92
|
+
# Sets the logger object in RedisMemo. Allows the logger to be dynamically
|
93
|
+
# determined at runtime if a blk is given.
|
49
94
|
def logger(&blk)
|
50
95
|
if blk.nil?
|
51
96
|
return @logger if @logger.respond_to?(:warn)
|
@@ -56,6 +101,8 @@ class RedisMemo::Options
|
|
56
101
|
end
|
57
102
|
end
|
58
103
|
|
104
|
+
# Sets the global cache key version. Allows the logger to be dynamically
|
105
|
+
# determined at runtime if a blk is given.
|
59
106
|
def global_cache_key_version(&blk)
|
60
107
|
# this method takes a block to be consistent with the inline memo_method
|
61
108
|
# API
|
@@ -71,16 +118,70 @@ class RedisMemo::Options
|
|
71
118
|
end
|
72
119
|
end
|
73
120
|
|
121
|
+
# Disables the model for caching and invalidation
|
122
|
+
def disable_model(model)
|
123
|
+
@disabled_models << model
|
124
|
+
end
|
125
|
+
|
126
|
+
# Checks if a model is disabled for redis memo caching
|
127
|
+
def model_disabled_for_caching?(model)
|
128
|
+
ENV["REDIS_MEMO_DISABLE_#{model.table_name.upcase}"] == 'true' || @disabled_models.include?(model)
|
129
|
+
end
|
130
|
+
|
131
|
+
# A handler used to asynchronously perform cache writes and invalidations. If no value is provided,
|
132
|
+
# RedisMemo will perform these operations synchronously.
|
74
133
|
attr_accessor :async
|
134
|
+
|
135
|
+
# Specify the global sampled percentage of the chance to call the cache validation, a value between 0 to 100, when the value
|
136
|
+
# is 100, it will call the handler every time the cached result does not match the uncached result
|
137
|
+
# You can also specify inline cache validation sample percentage by memoize_method :method, cache_validation_sample_percentage: #{value}
|
138
|
+
attr_accessor :cache_validation_sample_percentage
|
139
|
+
|
140
|
+
# Handler called when the cached result does not match the uncached result (sampled at the
|
141
|
+
# `cache_validation_sample_percentage`). This might indicate that invalidation is happening too slowly or
|
142
|
+
# that there are incorrect dependencies specified on a cached method.
|
75
143
|
attr_accessor :cache_out_of_date_handler
|
76
|
-
|
144
|
+
|
145
|
+
# Passed along to the Rails {RedisCacheStore}[https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html], determines whether or not to compress entries before storing
|
146
|
+
# them. default: `true`
|
77
147
|
attr_accessor :compress
|
148
|
+
|
149
|
+
# Passed along to the Rails {RedisCacheStore}[https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html], the size threshold for which to compress cached entries.
|
150
|
+
# default: 1.kilobyte
|
78
151
|
attr_accessor :compress_threshold
|
152
|
+
|
153
|
+
# Configuration values for connecting to RedisMemo using a connection pool. It's recommended to use a
|
154
|
+
# connection pool in multi-threaded applications, or when an async handler is set.
|
79
155
|
attr_accessor :connection_pool
|
156
|
+
|
157
|
+
# Passed along to the Rails {RedisCacheStore}[https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html], sets the TTL on cache entries in Redis.
|
80
158
|
attr_accessor :expires_in
|
159
|
+
|
160
|
+
# The max number of failed connection attempts RedisMemo will make for a single request before bypassing
|
161
|
+
# the caching layer. This helps make RedisMemo resilient to errors and performance issues when there's
|
162
|
+
# an issue with the Redis cluster itself.
|
163
|
+
attr_accessor :max_connection_attempts
|
164
|
+
|
165
|
+
# Passed along to the Rails {RedisCacheStore}[https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html], the error handler called for Redis related errors.
|
81
166
|
attr_accessor :redis_error_handler
|
82
167
|
|
168
|
+
# A global kill switch to disable all RedisMemo operations.
|
169
|
+
attr_accessor :disable_all
|
170
|
+
|
171
|
+
# A kill switch to disable RedisMemo caching on database queries. This does not disable the invalidation
|
172
|
+
# after_save hooks that are installed on memoized models.
|
173
|
+
attr_accessor :disable_cached_select
|
174
|
+
|
175
|
+
# A kill switch to set the list of models to disable caching and invalidation after_save hooks on.
|
176
|
+
attr_accessor :disabled_models
|
177
|
+
|
178
|
+
# A global cache key version prepended to each cached entry. For example, the commit hash of the current
|
179
|
+
# version deployed to your application.
|
83
180
|
attr_writer :global_cache_key_version
|
181
|
+
|
182
|
+
# Object used to trace RedisMemo operations to collect latency and error metrics, e.g. `Datadog.tracer`
|
84
183
|
attr_writer :tracer
|
184
|
+
|
185
|
+
# Object used to log RedisMemo operations.
|
85
186
|
attr_writer :logger
|
86
187
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RedisMemo::Railtie < Rails::Railtie
|
4
|
+
initializer 'request_store.insert_middleware' do |app|
|
5
|
+
if ActionDispatch.const_defined? :RequestId
|
6
|
+
app.config.middleware.insert_after ActionDispatch::RequestId, RedisMemo::Middleware
|
7
|
+
else
|
8
|
+
app.config.middleware.insert_after Rack::MethodOverride, RedisMemo::Middleware
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/redis_memo/redis.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'redis'
|
3
4
|
require 'redis/distributed'
|
4
5
|
|
@@ -7,7 +8,9 @@ require_relative 'options'
|
|
7
8
|
# Redis::Distributed does not support reading from multiple read replicas. This
|
8
9
|
# class adds this functionality
|
9
10
|
class RedisMemo::Redis < Redis::Distributed
|
10
|
-
def initialize(
|
11
|
+
def initialize(
|
12
|
+
options = {} # rubocop: disable Style/OptionHash
|
13
|
+
)
|
11
14
|
clients =
|
12
15
|
if options.is_a?(Array)
|
13
16
|
options.map do |option|
|
@@ -30,6 +33,17 @@ class RedisMemo::Redis < Redis::Distributed
|
|
30
33
|
super([], ring: hash_ring)
|
31
34
|
end
|
32
35
|
|
36
|
+
def run_script(script_content, script_sha, *args)
|
37
|
+
begin
|
38
|
+
return evalsha(script_sha, *args) if script_sha
|
39
|
+
rescue Redis::CommandError => error
|
40
|
+
if error.message != 'NOSCRIPT No matching script. Please use EVAL.'
|
41
|
+
raise error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
eval(script_content, *args) # rubocop: disable Security/Eval
|
45
|
+
end
|
46
|
+
|
33
47
|
class WithReplicas < ::Redis
|
34
48
|
def initialize(orig_options)
|
35
49
|
options = orig_options.dup
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Redis memo can be flaky due to transient network errors (e.g. Redis connection errors), or when
|
4
|
+
# used with async handlers. This class allows users to override the default redis-memo behavior
|
5
|
+
# to be more robust when testing their code that uses redis-memo.
|
6
|
+
module RedisMemo
|
7
|
+
class Testing
|
8
|
+
class << self
|
9
|
+
attr_accessor :__test_mode
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.enable_test_mode(&blk)
|
13
|
+
__set_test_mode(true, &blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.disable_test_mode(&blk)
|
17
|
+
__set_test_mode(false, &blk)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.enabled?
|
21
|
+
__test_mode
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.__set_test_mode(mode, &blk)
|
25
|
+
if blk.nil?
|
26
|
+
__test_mode = mode
|
27
|
+
else
|
28
|
+
prev_mode = __test_mode
|
29
|
+
begin
|
30
|
+
__test_mode = mode
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
__test_mode = prev_mode
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module TestOverrides
|
40
|
+
def without_memoization?
|
41
|
+
if RedisMemo::Testing.enabled? && !RedisMemo::Memoizable::Invalidation.class_variable_get(:@@invalidation_queue).empty?
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
singleton_class.prepend(TestOverrides)
|
49
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RedisMemo::ThreadLocalVar
|
4
|
+
def self.define(var_name) # :nodoc:
|
5
|
+
thread_key = :"__redis_memo_#{var_name}__"
|
6
|
+
const_set(var_name.to_s.upcase, thread_key)
|
7
|
+
|
8
|
+
define_singleton_method var_name do
|
9
|
+
Thread.current[thread_key]
|
10
|
+
end
|
11
|
+
|
12
|
+
define_singleton_method "#{var_name}=" do |var_val|
|
13
|
+
Thread.current[thread_key] = var_val
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/redis_memo/tracer.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RedisMemo::Util
|
4
|
+
def self.checksum(serialized)
|
5
|
+
Digest::SHA1.base64digest(serialized)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.deep_sort_hash(orig_hash)
|
9
|
+
{}.tap do |new_hash|
|
10
|
+
orig_hash.sort.each do |k, v|
|
11
|
+
new_hash[k] = v.is_a?(Hash) ? deep_sort_hash(v) : v
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.uuid
|
17
|
+
SecureRandom.uuid
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-memo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chan Zuckerberg Initiative
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: connection_pool
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.3
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: redis
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,19 +53,19 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: 4.0.1
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: ruby2_keywords
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
61
|
+
version: '0'
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: activerecord
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +136,20 @@ dependencies:
|
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: railties
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '5.2'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '5.2'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: rake
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +178,48 @@ dependencies:
|
|
150
178
|
- - "~>"
|
151
179
|
- !ruby/object:Gem::Version
|
152
180
|
version: '3.2'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rubocop
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rubocop-performance
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: rubocop-rspec
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
153
223
|
- !ruby/object:Gem::Dependency
|
154
224
|
name: simplecov
|
155
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,8 +246,10 @@ files:
|
|
176
246
|
- lib/redis_memo/batch.rb
|
177
247
|
- lib/redis_memo/cache.rb
|
178
248
|
- lib/redis_memo/connection_pool.rb
|
249
|
+
- lib/redis_memo/errors.rb
|
179
250
|
- lib/redis_memo/future.rb
|
180
251
|
- lib/redis_memo/memoizable.rb
|
252
|
+
- lib/redis_memo/memoizable/bump_version.lua
|
181
253
|
- lib/redis_memo/memoizable/dependency.rb
|
182
254
|
- lib/redis_memo/memoizable/invalidation.rb
|
183
255
|
- lib/redis_memo/memoize_method.rb
|
@@ -191,8 +263,12 @@ files:
|
|
191
263
|
- lib/redis_memo/memoize_query/model_callback.rb
|
192
264
|
- lib/redis_memo/middleware.rb
|
193
265
|
- lib/redis_memo/options.rb
|
266
|
+
- lib/redis_memo/railtie.rb
|
194
267
|
- lib/redis_memo/redis.rb
|
268
|
+
- lib/redis_memo/testing.rb
|
269
|
+
- lib/redis_memo/thread_local_var.rb
|
195
270
|
- lib/redis_memo/tracer.rb
|
271
|
+
- lib/redis_memo/util.rb
|
196
272
|
homepage: https://github.com/chanzuckerberg/redis-memo
|
197
273
|
licenses:
|
198
274
|
- MIT
|