Module: IdentityCache

Extended by:
ActiveSupport::Concern, CacheHash
Includes:
WithPrimaryIndex
Defined in:
lib/identity_cache.rb,
lib/identity_cache/cached.rb,
lib/identity_cache/encoder.rb,
lib/identity_cache/railtie.rb,
lib/identity_cache/version.rb,
lib/identity_cache/query_api.rb,
lib/identity_cache/cache_hash.rb,
lib/identity_cache/expiry_hook.rb,
lib/identity_cache/cache_fetcher.rb,
lib/identity_cache/cache_key_loader.rb,
lib/identity_cache/cached/attribute.rb,
lib/identity_cache/fallback_fetcher.rb,
lib/identity_cache/record_not_found.rb,
lib/identity_cache/should_use_cache.rb,
lib/identity_cache/cached/belongs_to.rb,
lib/identity_cache/cached/prefetcher.rb,
lib/identity_cache/configuration_dsl.rb,
lib/identity_cache/belongs_to_caching.rb,
lib/identity_cache/cache_invalidation.rb,
lib/identity_cache/cached/association.rb,
lib/identity_cache/load_strategy/lazy.rb,
lib/identity_cache/with_primary_index.rb,
lib/identity_cache/load_strategy/eager.rb,
lib/identity_cache/mem_cache_store_cas.rb,
lib/identity_cache/cache_key_generation.rb,
lib/identity_cache/cached/primary_index.rb,
lib/identity_cache/memoized_cache_proxy.rb,
lib/identity_cache/without_primary_index.rb,
lib/identity_cache/cached/attribute_by_one.rb,
lib/identity_cache/parent_model_expiration.rb,
lib/identity_cache/cached/embedded_fetching.rb,
lib/identity_cache/cached/recursive/has_one.rb,
lib/identity_cache/cached/reference/has_one.rb,
lib/identity_cache/cached/attribute_by_multi.rb,
lib/identity_cache/cached/recursive/has_many.rb,
lib/identity_cache/cached/reference/has_many.rb,
lib/identity_cache/load_strategy/load_request.rb,
lib/identity_cache/cached/recursive/association.rb,
lib/identity_cache/cached/reference/association.rb,
lib/identity_cache/load_strategy/multi_load_request.rb

Defined Under Namespace

Modules: BelongsToCaching, CacheHash, CacheInvalidation, CacheKeyGeneration, ConfigurationDSL, LoadStrategy, MemCacheStoreCAS, QueryAPI, ShouldUseCache, WithPrimaryIndex, WithoutPrimaryIndex Classes: AlreadyIncludedError, AssociationError, CacheFetcher, DerivedModelError, FallbackFetcher, InverseAssociationError, LockWaitTimeout, MemoizedCacheProxy, NestedDeferredCacheExpirationBlockError, NestedDeferredParentBlockError, Railtie, RecordNotFound, UnsupportedAssociationError, UnsupportedScopeError

Constant Summary collapse

CACHED_NIL =
:idc_cached_nil
BATCH_SIZE =
1000
DELETED =
:idc_cached_deleted
DELETED_TTL =
1000
VERSION =
"1.6.3"
CACHE_VERSION =
8

Constants included from CacheInvalidation

CacheInvalidation::CACHE_KEY_NAMES

Constants included from CacheKeyGeneration

CacheKeyGeneration::DEFAULT_NAMESPACE

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CacheHash

memcache_hash

Methods included from WithPrimaryIndex

#expire_cache, #expire_primary_index, #primary_cache_index_key

Methods included from CacheInvalidation

#reload

Methods included from QueryAPI

#_run_commit_callbacks, #expire_cache, #was_new_record?

Methods included from CacheKeyGeneration

denormalized_schema_hash, denormalized_schema_string, schema_to_string

Class Attribute Details

.fetch_read_only_recordsObject



308
309
310
311
312
313
# File 'lib/identity_cache.rb', line 308

def fetch_read_only_records
  v = Thread.current[:identity_cache_fetch_read_only_records]
  return v unless v.nil?

  @fetch_read_only_records
end

.loggerObject



114
115
116
# File 'lib/identity_cache.rb', line 114

def logger
  @logger || Rails.logger
end

.readonlyObject

Returns the value of attribute readonly.



87
88
89
# File 'lib/identity_cache.rb', line 87

def readonly
  @readonly
end

Class Method Details

.append_features(base) ⇒ Object

:nodoc:



90
91
92
93
94
# File 'lib/identity_cache.rb', line 90

def append_features(base) # :nodoc:
  raise AlreadyIncludedError if base.include?(IdentityCache)

  super
end

.cacheObject



110
111
112
# File 'lib/identity_cache.rb', line 110

def cache
  @cache ||= MemoizedCacheProxy.new
end

.cache_backend=(cache_adaptor) ⇒ Object

Sets the cache adaptor IdentityCache will be using

Parameters

cache_adaptor - A ActiveSupport::Cache::Store



102
103
104
105
106
107
108
# File 'lib/identity_cache.rb', line 102

def cache_backend=(cache_adaptor)
  if defined?(@cache)
    cache.cache_backend = cache_adaptor
  else
    @cache = MemoizedCacheProxy.new(cache_adaptor)
  end
end

.deprecatorObject



319
320
321
# File 'lib/identity_cache.rb', line 319

def deprecator
  @deprecator ||= ActiveSupport::Deprecation.new("1.7.0", "IdentityCache")
end

.eager_load!Object



315
316
317
# File 'lib/identity_cache.rb', line 315

def eager_load!
  ParentModelExpiration.install_all_pending_parent_expiry_hooks
end

.fetch(key, cache_fetcher_options = {}) ⇒ Object

Cache retrieval and miss resolver primitive; given a key it will try to retrieve the associated value from the cache otherwise it will return the value of the execution of the block.

Parameters

key A cache key string cache_fetcher_options A hash of options to pass to the cache backend



163
164
165
166
167
168
169
170
171
# File 'lib/identity_cache.rb', line 163

def fetch(key, cache_fetcher_options = {})
  if should_use_cache?
    unmap_cached_nil_for(cache.fetch(key, cache_fetcher_options) do
      map_cached_nil_for(yield)
    end)
  else
    yield
  end
end

.fetch_multi(*keys) ⇒ Object

Same as fetch, except that it will try a collection of keys, using the multiget operation of the cache adaptor.

Parameters

keys A collection or array of key strings



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/identity_cache.rb', line 186

def fetch_multi(*keys)
  keys.flatten!(1)
  return {} if keys.empty?

  result = if should_use_cache?
    fetch_in_batches(keys.uniq) do |missed_keys|
      results = yield missed_keys
      results.map { |e| map_cached_nil_for(e) }
    end
  else
    results = yield keys
    Hash[keys.zip(results)]
  end

  result.each do |key, value|
    result[key] = unmap_cached_nil_for(value)
  end

  result
end

.map_cached_nil_for(value) ⇒ Object



173
174
175
# File 'lib/identity_cache.rb', line 173

def map_cached_nil_for(value)
  value.nil? ? IdentityCache::CACHED_NIL : value
end

.should_fill_cache?Boolean

:nodoc:

Returns:

  • (Boolean)


118
119
120
# File 'lib/identity_cache.rb', line 118

def should_fill_cache? # :nodoc:
  !readonly
end

.unmap_cached_nil_for(value) ⇒ Object



177
178
179
# File 'lib/identity_cache.rb', line 177

def unmap_cached_nil_for(value)
  value == IdentityCache::CACHED_NIL ? nil : value
end

.with_deferred_expirationObject

Executes a block with deferred cache expiration, ensuring that the records’ (parent, children and attributes) cache expiration is deferred until the block completes. When the block completes, it issues delete_multi calls for all the records and attributes that were marked for expiration.

Parameters:

No parameters.

Raises:

NestedDeferredCacheExpirationBlockError if a deferred cache expiration block is already active.

Yield:

Runs the provided block with deferred cache expiration.

Returns:

The result of executing the provided block.

Ensures:

Cleans up thread-local variables related to deferred cache expiration regardless of whether the block raises an exception.



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/identity_cache.rb', line 269

def with_deferred_expiration
  raise NestedDeferredCacheExpirationBlockError if Thread.current[:idc_deferred_expiration]

  if Thread.current[:idc_deferred_parent_expiration]
    deprecator.deprecation_warning("`with_deferred_parent_expiration`")
  end

  Thread.current[:idc_deferred_expiration] = true
  Thread.current[:idc_records_to_expire] = Set.new
  Thread.current[:idc_attributes_to_expire] = Set.new

  result = yield

  Thread.current[:idc_deferred_expiration] = nil
  if Thread.current[:idc_records_to_expire].any?
    IdentityCache.cache.delete_multi(
      Thread.current[:idc_records_to_expire]
    )
  end
  if Thread.current[:idc_attributes_to_expire].any?
    IdentityCache.cache.delete_multi(
      Thread.current[:idc_attributes_to_expire]
    )
  end
  result
ensure
  Thread.current[:idc_deferred_expiration] = nil
  Thread.current[:idc_records_to_expire].clear
  Thread.current[:idc_attributes_to_expire].clear
end

.with_deferred_parent_expirationObject

Executes a block with deferred parent expiration, ensuring that the parent records’ cache expiration is deferred until the block completes. When the block completes, it triggers expiration of the primary index for the parent records. Raises a NestedDeferredParentBlockError if a deferred parent expiration block is already active on the current thread.

Parameters:

No parameters.

Raises:

NestedDeferredParentBlockError if a deferred parent expiration block is already active.

Yield:

Runs the provided block with deferred parent expiration.

Returns:

The result of executing the provided block.

Ensures:

Cleans up thread-local variables related to deferred parent expiration regardless of whether the block raises an exception.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/identity_cache.rb', line 228

def with_deferred_parent_expiration
  raise NestedDeferredParentBlockError if Thread.current[:idc_deferred_parent_expiration]

  if Thread.current[:idc_deferred_expiration]
    deprecator.deprecation_warning("`with_deferred_parent_expiration`")
  end

  Thread.current[:idc_deferred_parent_expiration] = true
  Thread.current[:idc_parent_records_for_cache_expiry] = Set.new

  result = yield

  Thread.current[:idc_deferred_parent_expiration] = nil
  Thread.current[:idc_parent_records_for_cache_expiry].each(&:expire_primary_index)

  result
ensure
  Thread.current[:idc_deferred_parent_expiration] = nil
  Thread.current[:idc_parent_records_for_cache_expiry]&.clear
end

.with_fetch_read_only_records(value = true) ⇒ Object



300
301
302
303
304
305
306
# File 'lib/identity_cache.rb', line 300

def with_fetch_read_only_records(value = true)
  old_value = Thread.current[:identity_cache_fetch_read_only_records]
  Thread.current[:identity_cache_fetch_read_only_records] = value
  yield
ensure
  Thread.current[:identity_cache_fetch_read_only_records] = old_value
end

Instance Method Details

#should_use_cache?Boolean

:nodoc:

Returns:

  • (Boolean)


140
141
142
143
144
145
# File 'lib/identity_cache.rb', line 140

def should_use_cache? # :nodoc:
  ActiveRecord::Base.connection_handler.connection_pool_list(ActiveRecord::Base.current_role).none? do |pool|
    pool.active_connection? &&
      pool.active_connection.current_transaction.joinable?
  end
end