identity_cache 1.2.0 → 1.6.3

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +8 -7
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +0 -1
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +84 -2
  7. data/Gemfile +4 -4
  8. data/Gemfile.lock +118 -0
  9. data/README.md +28 -5
  10. data/Rakefile +98 -6
  11. data/dev.yml +17 -17
  12. data/gemfiles/Gemfile.latest-release +1 -0
  13. data/gemfiles/Gemfile.min-supported +5 -5
  14. data/gemfiles/Gemfile.rails-edge +1 -0
  15. data/identity_cache.gemspec +3 -3
  16. data/lib/identity_cache/belongs_to_caching.rb +1 -1
  17. data/lib/identity_cache/cache_fetcher.rb +12 -3
  18. data/lib/identity_cache/cache_key_loader.rb +1 -1
  19. data/lib/identity_cache/cached/attribute.rb +66 -3
  20. data/lib/identity_cache/cached/attribute_by_multi.rb +94 -9
  21. data/lib/identity_cache/cached/attribute_by_one.rb +4 -40
  22. data/lib/identity_cache/cached/belongs_to.rb +14 -10
  23. data/lib/identity_cache/cached/primary_index.rb +10 -2
  24. data/lib/identity_cache/cached/recursive/association.rb +1 -1
  25. data/lib/identity_cache/cached/reference/has_many.rb +1 -1
  26. data/lib/identity_cache/configuration_dsl.rb +1 -1
  27. data/lib/identity_cache/encoder.rb +1 -0
  28. data/lib/identity_cache/fallback_fetcher.rb +5 -1
  29. data/lib/identity_cache/memoized_cache_proxy.rb +10 -0
  30. data/lib/identity_cache/parent_model_expiration.rb +8 -3
  31. data/lib/identity_cache/query_api.rb +12 -6
  32. data/lib/identity_cache/should_use_cache.rb +10 -0
  33. data/lib/identity_cache/version.rb +1 -1
  34. data/lib/identity_cache/with_primary_index.rb +27 -14
  35. data/lib/identity_cache.rb +131 -21
  36. data/shipit.rubygems.yml +4 -1
  37. metadata +13 -12
  38. data/isogun.yml +0 -11
@@ -60,6 +60,10 @@ module IdentityCache
60
60
 
61
61
  class InverseAssociationError < StandardError; end
62
62
 
63
+ class NestedDeferredParentBlockError < StandardError; end
64
+
65
+ class NestedDeferredCacheExpirationBlockError < StandardError; end
66
+
63
67
  class UnsupportedScopeError < StandardError; end
64
68
 
65
69
  class UnsupportedAssociationError < StandardError; end
@@ -115,27 +119,36 @@ module IdentityCache
115
119
  !readonly
116
120
  end
117
121
 
118
- def should_use_cache? # :nodoc:
119
- ActiveRecord::Base.connection_handler.connection_pool_list.none? do |pool|
120
- pool.active_connection? &&
121
- # Rails wraps each of your tests in a transaction, so that any changes
122
- # made to the database during the test can be rolled back afterwards.
123
- # These transactions are flagged as "unjoinable", which tries to make
124
- # your application behave as if they weren't there. In particular:
125
- #
126
- # - Opening another transaction during the test creates a savepoint,
127
- # which can be rolled back independently of the main transaction.
128
- # - When those nested transactions complete, any `after_commit`
129
- # callbacks for records modified during the transaction will run,
130
- # even though the changes haven't actually been committed yet.
131
- #
132
- # By ignoring unjoinable transactions, IdentityCache's behaviour
133
- # during your test suite will more closely match production.
134
- #
135
- # When there are no open transactions, `current_transaction` returns a
136
- # special `NullTransaction` object that is unjoinable, meaning we will
137
- # use the cache.
138
- pool.connection.current_transaction.joinable?
122
+ # Rails wraps each of your tests in a transaction, so that any changes
123
+ # made to the database during the test can be rolled back afterwards.
124
+ # These transactions are flagged as "unjoinable", which tries to make
125
+ # your application behave as if they weren't there. In particular:
126
+ #
127
+ # - Opening another transaction during the test creates a savepoint,
128
+ # which can be rolled back independently of the main transaction.
129
+ # - When those nested transactions complete, any `after_commit`
130
+ # callbacks for records modified during the transaction will run,
131
+ # even though the changes haven't actually been committed yet.
132
+ #
133
+ # By ignoring unjoinable transactions, IdentityCache's behaviour
134
+ # during your test suite will more closely match production.
135
+ #
136
+ # When there are no open transactions, `current_transaction` returns a
137
+ # special `NullTransaction` object that is unjoinable, meaning we will
138
+ # use the cache.
139
+ if ActiveRecord::ConnectionAdapters::ConnectionPool.method_defined?(:active_connection)
140
+ def should_use_cache? # :nodoc:
141
+ ActiveRecord::Base.connection_handler.connection_pool_list(ActiveRecord::Base.current_role).none? do |pool|
142
+ pool.active_connection? &&
143
+ pool.active_connection.current_transaction.joinable?
144
+ end
145
+ end
146
+ else
147
+ def should_use_cache? # :nodoc:
148
+ ActiveRecord::Base.connection_handler.connection_pool_list(ActiveRecord::Base.current_role).none? do |pool|
149
+ pool.active_connection? &&
150
+ pool.connection.current_transaction.joinable?
151
+ end
139
152
  end
140
153
  end
141
154
 
@@ -191,6 +204,99 @@ module IdentityCache
191
204
  result
192
205
  end
193
206
 
207
+ # Executes a block with deferred parent expiration, ensuring that the parent
208
+ # records' cache expiration is deferred until the block completes. When the block
209
+ # completes, it triggers expiration of the primary index for the parent records.
210
+ # Raises a NestedDeferredParentBlockError if a deferred parent expiration block
211
+ # is already active on the current thread.
212
+ #
213
+ # == Parameters:
214
+ # No parameters.
215
+ #
216
+ # == Raises:
217
+ # NestedDeferredParentBlockError if a deferred parent expiration block is already active.
218
+ #
219
+ # == Yield:
220
+ # Runs the provided block with deferred parent expiration.
221
+ #
222
+ # == Returns:
223
+ # The result of executing the provided block.
224
+ #
225
+ # == Ensures:
226
+ # Cleans up thread-local variables related to deferred parent expiration regardless
227
+ # of whether the block raises an exception.
228
+ def with_deferred_parent_expiration
229
+ raise NestedDeferredParentBlockError if Thread.current[:idc_deferred_parent_expiration]
230
+
231
+ if Thread.current[:idc_deferred_expiration]
232
+ deprecator.deprecation_warning("`with_deferred_parent_expiration`")
233
+ end
234
+
235
+ Thread.current[:idc_deferred_parent_expiration] = true
236
+ Thread.current[:idc_parent_records_for_cache_expiry] = Set.new
237
+
238
+ result = yield
239
+
240
+ Thread.current[:idc_deferred_parent_expiration] = nil
241
+ Thread.current[:idc_parent_records_for_cache_expiry].each(&:expire_primary_index)
242
+
243
+ result
244
+ ensure
245
+ Thread.current[:idc_deferred_parent_expiration] = nil
246
+ Thread.current[:idc_parent_records_for_cache_expiry]&.clear
247
+ end
248
+
249
+ # Executes a block with deferred cache expiration, ensuring that the records' (parent,
250
+ # children and attributes) cache expiration is deferred until the block completes. When
251
+ # the block completes, it issues delete_multi calls for all the records and attributes
252
+ # that were marked for expiration.
253
+ #
254
+ # == Parameters:
255
+ # No parameters.
256
+ #
257
+ # == Raises:
258
+ # NestedDeferredCacheExpirationBlockError if a deferred cache expiration block is already active.
259
+ #
260
+ # == Yield:
261
+ # Runs the provided block with deferred cache expiration.
262
+ #
263
+ # == Returns:
264
+ # The result of executing the provided block.
265
+ #
266
+ # == Ensures:
267
+ # Cleans up thread-local variables related to deferred cache expiration regardless
268
+ # of whether the block raises an exception.
269
+ def with_deferred_expiration
270
+ raise NestedDeferredCacheExpirationBlockError if Thread.current[:idc_deferred_expiration]
271
+
272
+ if Thread.current[:idc_deferred_parent_expiration]
273
+ deprecator.deprecation_warning("`with_deferred_parent_expiration`")
274
+ end
275
+
276
+ Thread.current[:idc_deferred_expiration] = true
277
+ Thread.current[:idc_records_to_expire] = Set.new
278
+ Thread.current[:idc_attributes_to_expire] = Set.new
279
+
280
+ result = yield
281
+
282
+ Thread.current[:idc_deferred_expiration] = nil
283
+ if Thread.current[:idc_records_to_expire].any?
284
+ IdentityCache.cache.delete_multi(
285
+ Thread.current[:idc_records_to_expire]
286
+ )
287
+ end
288
+ if Thread.current[:idc_attributes_to_expire].any?
289
+ IdentityCache.cache.delete_multi(
290
+ Thread.current[:idc_attributes_to_expire]
291
+ )
292
+ end
293
+ result
294
+ ensure
295
+ Thread.current[:idc_deferred_expiration] = nil
296
+ Thread.current[:idc_records_to_expire].clear
297
+ Thread.current[:idc_attributes_to_expire].clear
298
+ end
299
+
194
300
  def with_fetch_read_only_records(value = true)
195
301
  old_value = Thread.current[:identity_cache_fetch_read_only_records]
196
302
  Thread.current[:identity_cache_fetch_read_only_records] = value
@@ -210,6 +316,10 @@ module IdentityCache
210
316
  ParentModelExpiration.install_all_pending_parent_expiry_hooks
211
317
  end
212
318
 
319
+ def deprecator
320
+ @deprecator ||= ActiveSupport::Deprecation.new("1.7.0", "IdentityCache")
321
+ end
322
+
213
323
  private
214
324
 
215
325
  def fetch_in_batches(keys, &block)
data/shipit.rubygems.yml CHANGED
@@ -1 +1,4 @@
1
- # using the default shipit config
1
+ dependencies:
2
+ bundler:
3
+ without:
4
+ - default
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: identity_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camilo Lopez
@@ -11,10 +11,10 @@ authors:
11
11
  - Tobias Lutke
12
12
  - Arthur Neves
13
13
  - Francis Bogsanyi
14
- autorequire:
14
+ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2022-08-15 00:00:00.000000000 Z
17
+ date: 2024-10-22 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activerecord
@@ -22,14 +22,14 @@ dependencies:
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '5.2'
25
+ version: '7.0'
26
26
  type: :runtime
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '5.2'
32
+ version: '7.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: ar_transaction_changes
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.12'
67
+ version: '2.0'
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '1.12'
74
+ version: '2.0'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: rake
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -111,12 +111,14 @@ files:
111
111
  - ".github/workflows/cla.yml"
112
112
  - ".gitignore"
113
113
  - ".rubocop.yml"
114
+ - ".ruby-version"
114
115
  - ".spin/bootstrap"
115
116
  - ".spin/svc.yml"
116
117
  - CAVEATS.md
117
118
  - CHANGELOG.md
118
119
  - CONTRIBUTING.md
119
120
  - Gemfile
121
+ - Gemfile.lock
120
122
  - LICENSE
121
123
  - README.md
122
124
  - Rakefile
@@ -125,7 +127,6 @@ files:
125
127
  - gemfiles/Gemfile.min-supported
126
128
  - gemfiles/Gemfile.rails-edge
127
129
  - identity_cache.gemspec
128
- - isogun.yml
129
130
  - lib/identity_cache.rb
130
131
  - lib/identity_cache/belongs_to_caching.rb
131
132
  - lib/identity_cache/cache_fetcher.rb
@@ -175,7 +176,7 @@ homepage: https://github.com/Shopify/identity_cache
175
176
  licenses: []
176
177
  metadata:
177
178
  allowed_push_host: https://rubygems.org
178
- post_install_message:
179
+ post_install_message:
179
180
  rdoc_options: []
180
181
  require_paths:
181
182
  - lib
@@ -183,15 +184,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
183
184
  requirements:
184
185
  - - ">="
185
186
  - !ruby/object:Gem::Version
186
- version: 2.5.0
187
+ version: 3.0.0
187
188
  required_rubygems_version: !ruby/object:Gem::Requirement
188
189
  requirements:
189
190
  - - ">="
190
191
  - !ruby/object:Gem::Version
191
192
  version: '0'
192
193
  requirements: []
193
- rubygems_version: 3.3.3
194
- signing_key:
194
+ rubygems_version: 3.5.22
195
+ signing_key:
195
196
  specification_version: 4
196
197
  summary: IdentityCache lets you specify how you want to cache your model objects,
197
198
  at the model level, and adds a number of convenience methods for accessing those
data/isogun.yml DELETED
@@ -1,11 +0,0 @@
1
- # https://dev-accel.shopify.io/dev/railgun/Railgun-Config
2
- name: identity-cache
3
-
4
- vm:
5
- ip_address: 192.168.64.98
6
- memory: 1G
7
- cores: 2
8
-
9
- services:
10
- - mysql
11
- - memcached