redis-memo 0.0.0.beta → 0.0.0.beta.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,7 +31,8 @@ class RedisMemo::Redis < Redis::Distributed
31
31
  end
32
32
 
33
33
  class WithReplicas < ::Redis
34
- def initialize(options)
34
+ def initialize(orig_options)
35
+ options = orig_options.dup
35
36
  primary_option = options.shift
36
37
  @replicas = options.map do |option|
37
38
  option[:logger] ||= RedisMemo::DefaultOptions.logger
@@ -11,13 +11,15 @@ class RedisMemo::Tracer
11
11
  end
12
12
  end
13
13
 
14
- def self.set_tag(cache_hit:)
14
+ def self.set_tag(**tags)
15
15
  tracer = RedisMemo::DefaultOptions.tracer
16
16
  return if tracer.nil? || !tracer.respond_to?(:active_span)
17
17
 
18
18
  active_span = tracer.active_span
19
19
  return if !active_span.respond_to?(:set_tag)
20
20
 
21
- active_span.set_tag('cache_hit', cache_hit)
21
+ tags.each do |name, value|
22
+ active_span.set_tag(name, value)
23
+ end
22
24
  end
23
25
  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: 0.0.0.beta
4
+ version: 0.0.0.beta.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chan Zuckerberg Initiative
@@ -14,44 +14,72 @@ dependencies:
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: redis
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '4'
33
+ version: 4.0.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: connection_pool
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.2.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '4'
54
+ version: 2.2.3
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: activerecord
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '5.2'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '5.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activerecord-import
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: codecov
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -147,16 +175,20 @@ files:
147
175
  - lib/redis_memo/after_commit.rb
148
176
  - lib/redis_memo/batch.rb
149
177
  - lib/redis_memo/cache.rb
178
+ - lib/redis_memo/connection_pool.rb
150
179
  - lib/redis_memo/future.rb
151
180
  - lib/redis_memo/memoizable.rb
152
181
  - lib/redis_memo/memoizable/dependency.rb
153
182
  - lib/redis_memo/memoizable/invalidation.rb
154
183
  - lib/redis_memo/memoize_method.rb
155
- - lib/redis_memo/memoize_method.rbi
156
- - lib/redis_memo/memoize_records.rb
157
- - lib/redis_memo/memoize_records/cached_select.rb
158
- - lib/redis_memo/memoize_records/invalidation.rb
159
- - lib/redis_memo/memoize_records/model_callback.rb
184
+ - lib/redis_memo/memoize_query.rb
185
+ - lib/redis_memo/memoize_query/cached_select.rb
186
+ - lib/redis_memo/memoize_query/cached_select/bind_params.rb
187
+ - lib/redis_memo/memoize_query/cached_select/connection_adapter.rb
188
+ - lib/redis_memo/memoize_query/cached_select/statement_cache.rb
189
+ - lib/redis_memo/memoize_query/invalidation.rb
190
+ - lib/redis_memo/memoize_query/memoize_table_column.rb
191
+ - lib/redis_memo/memoize_query/model_callback.rb
160
192
  - lib/redis_memo/middleware.rb
161
193
  - lib/redis_memo/options.rb
162
194
  - lib/redis_memo/redis.rb
@@ -1,10 +0,0 @@
1
- # typed: true
2
-
3
- module RedisMemo::MemoizeMethod
4
- include Kernel
5
-
6
- def class; end
7
- def name; end
8
- def alias_method(*); end
9
- def define_method(*); end
10
- end
@@ -1,146 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative 'memoize_method'
3
-
4
- #
5
- # Automatically invalidate memoizable when modifying ActiveRecords objects.
6
- # You still need to invalidate memos when you are using SQL queries to perform
7
- # update / delete (does not trigger record callbacks)
8
- #
9
- module RedisMemo::MemoizeRecords
10
- require_relative 'memoize_records/cached_select'
11
- require_relative 'memoize_records/invalidation'
12
- require_relative 'memoize_records/model_callback'
13
-
14
- # TODO: MemoizeRecords -> MemoizeQuery
15
- def memoize_records
16
- RedisMemo::MemoizeRecords.using_active_record!(self)
17
-
18
- memoize_table_column(primary_key.to_sym, editable: false)
19
- end
20
-
21
- # Only editable columns will be used to create memos that are invalidatable
22
- # after each record save
23
- def memoize_table_column(*raw_columns, editable: true)
24
- RedisMemo::MemoizeRecords.using_active_record!(self)
25
-
26
- columns = raw_columns.map(&:to_sym).sort
27
-
28
- RedisMemo::MemoizeRecords.memoized_columns(self, editable_only: true) << columns if editable
29
- RedisMemo::MemoizeRecords.memoized_columns(self, editable_only: false) << columns
30
-
31
- RedisMemo::MemoizeRecords::ModelCallback.install(self)
32
- RedisMemo::MemoizeRecords::Invalidation.install(self)
33
-
34
- if ENV['REDIS_MEMO_DISABLE_CACHED_SELECT'] != 'true'
35
- RedisMemo::MemoizeRecords::CachedSelect.install(ActiveRecord::Base.connection)
36
- end
37
-
38
- columns.each do |column|
39
- unless self.columns_hash.include?(column.to_s)
40
- raise(
41
- RedisMemo::ArgumentError,
42
- "'#{self.name}' does not contain column '#{column}'",
43
- )
44
- end
45
- end
46
- rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid
47
- # no-opts: models with memoize_table_column decleared might be loaded in
48
- # rake tasks that are used to create databases
49
- end
50
-
51
- def self.using_active_record!(model_class)
52
- unless model_class.respond_to?(:<) && model_class < ActiveRecord::Base
53
- raise RedisMemo::ArgumentError, "'#{model_class.name}' does not use ActiveRecord"
54
- end
55
- end
56
-
57
- @@memoized_columns = Hash.new { |h, k| h[k] = [Set.new, Set.new] }
58
-
59
- def self.memoized_columns(model_or_table, editable_only: false)
60
- table = model_or_table.is_a?(Class) ? model_or_table.table_name : model_or_table
61
- @@memoized_columns[table.to_sym][editable_only ? 1 : 0]
62
- end
63
-
64
- # extra_props are considered as AND conditions on the model class
65
- def self.create_memo(model_class, **extra_props)
66
- RedisMemo::MemoizeRecords.using_active_record!(model_class)
67
-
68
- keys = extra_props.keys.sort
69
- if !keys.empty? && !RedisMemo::MemoizeRecords.memoized_columns(model_class).include?(keys)
70
- raise(
71
- RedisMemo::ArgumentError,
72
- "'#{model_class.name}' has not memoized columns: #{keys}",
73
- )
74
- end
75
-
76
- extra_props.each do |key, values|
77
- # The data type is ensured by the database, thus we don't need to cast
78
- # types here for better performance
79
- column_name = key.to_s
80
- values = [values] unless values.is_a?(Enumerable)
81
- extra_props[key] =
82
- if model_class.defined_enums.include?(column_name)
83
- enum_mapping = model_class.defined_enums[column_name]
84
- values.map do |value|
85
- # Assume a value is a converted enum if it does not exist in the
86
- # enum mapping
87
- (enum_mapping[value.to_s] || value).to_s
88
- end
89
- else
90
- values.map(&:to_s)
91
- end
92
- end
93
-
94
- RedisMemo::Memoizable.new(
95
- __redis_memo_memoize_records_model_class_name__: model_class.name,
96
- **extra_props,
97
- )
98
- end
99
-
100
- def self.invalidate_all(model_class)
101
- RedisMemo::Tracer.trace(
102
- 'redis_memo.memoizable.invalidate_all',
103
- model_class.name,
104
- ) do
105
- RedisMemo::Memoizable.invalidate([model_class.redis_memo_class_memoizable])
106
- end
107
- end
108
-
109
- def self.invalidate(record)
110
- # Invalidate memos with current values
111
- memos_to_invalidate = memoized_columns(record.class).map do |columns|
112
- props = {}
113
- columns.each do |column|
114
- props[column] = record.send(column)
115
- end
116
-
117
- RedisMemo::MemoizeRecords.create_memo(record.class, **props)
118
- end
119
-
120
- # Create memos with previous values if
121
- # - there are saved changes
122
- # - this is not creating a new record
123
- if !record.saved_changes.empty? && !record.saved_changes.include?(record.class.primary_key)
124
- previous_values = {}
125
- record.saved_changes.each do |column, (previous_value, _)|
126
- previous_values[column.to_sym] = previous_value
127
- end
128
-
129
- memoized_columns(record.class, editable_only: true).each do |columns|
130
- props = previous_values.slice(*columns)
131
- next if props.empty?
132
-
133
- # Fill the column values that have not changed
134
- columns.each do |column|
135
- next if props.include?(column)
136
-
137
- props[column] = record.send(column)
138
- end
139
-
140
- memos_to_invalidate << RedisMemo::MemoizeRecords.create_memo(record.class, **props)
141
- end
142
- end
143
-
144
- RedisMemo::Memoizable.invalidate(memos_to_invalidate)
145
- end
146
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class RedisMemo::MemoizeRecords::Invalidation
4
- def self.install(model_class)
5
- var_name = :@@__redis_memo_memoize_records_invalidation_installed__
6
- return if model_class.class_variable_defined?(var_name)
7
-
8
- model_class.class_eval do
9
- # A memory-persistent memoizable used for invalidating all queries of a
10
- # particular model
11
- def self.redis_memo_class_memoizable
12
- @redis_memo_class_memoizable ||= RedisMemo::MemoizeRecords.create_memo(self)
13
- end
14
-
15
- %i(delete decrement! increment!).each do |method_name|
16
- alias_method :"without_redis_memo_invalidation_#{method_name}", method_name
17
-
18
- define_method method_name do |*args|
19
- result = send(:"without_redis_memo_invalidation_#{method_name}", *args)
20
-
21
- RedisMemo::MemoizeRecords.invalidate(self)
22
-
23
- result
24
- end
25
- end
26
- end
27
-
28
- # Methods that won't trigger model callbacks
29
- # https://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
30
- %i(
31
- import
32
- decrement_counter
33
- delete_all delete_by
34
- increment_counter
35
- insert insert! insert_all insert_all!
36
- touch_all
37
- update_column update_columns update_all update_counters
38
- upsert upsert_all
39
- ).each do |method_name|
40
- # Example: Model.update_all
41
- rewrite_bulk_update_method(
42
- model_class,
43
- model_class,
44
- method_name,
45
- class_method: true,
46
- )
47
-
48
- # Example: Model.where(...).update_all
49
- rewrite_bulk_update_method(
50
- model_class,
51
- model_class.const_get(:ActiveRecord_Relation),
52
- method_name,
53
- class_method: false,
54
- )
55
- end
56
-
57
- model_class.class_variable_set(var_name, true)
58
- end
59
-
60
- private
61
-
62
- #
63
- # There’s no good way to perform fine-grind cache invalidation when operations
64
- # are bulk update operations such as import, update_all, and destroy_all:
65
- # Performing fine-grind cache invalidation would require the applications to
66
- # fetch additional data from the database, which might lead to performance
67
- # degradation. Thus we simply invalidate all existing cached records after each
68
- # bulk_updates.
69
- #
70
- def self.rewrite_bulk_update_method(model_class, klass, method_name, class_method:)
71
- methods = class_method ? :methods : :instance_methods
72
- return unless klass.send(methods).include?(method_name)
73
-
74
- klass = klass.singleton_class if class_method
75
- klass.class_eval do
76
- alias_method :"#{method_name}_without_redis_memo_invalidation", method_name
77
-
78
- define_method method_name do |*args|
79
- result = send(:"#{method_name}_without_redis_memo_invalidation", *args)
80
- RedisMemo::MemoizeRecords.invalidate_all(model_class)
81
- result
82
- end
83
- end
84
- end
85
- end