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

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.
@@ -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