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, 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.0"
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

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



242
243
244
245
246
247
# File 'lib/identity_cache.rb', line 242

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



112
113
114
# File 'lib/identity_cache.rb', line 112

def logger
  @logger || Rails.logger
end

.readonlyObject

Returns the value of attribute readonly.



85
86
87
# File 'lib/identity_cache.rb', line 85

def readonly
  @readonly
end

Class Method Details

.append_features(base) ⇒ Object

:nodoc:



88
89
90
91
92
# File 'lib/identity_cache.rb', line 88

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

  super
end

.cacheObject



108
109
110
# File 'lib/identity_cache.rb', line 108

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



100
101
102
103
104
105
106
# File 'lib/identity_cache.rb', line 100

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

.eager_load!Object



249
250
251
# File 'lib/identity_cache.rb', line 249

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



152
153
154
155
156
157
158
159
160
# File 'lib/identity_cache.rb', line 152

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



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/identity_cache.rb', line 175

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



162
163
164
# File 'lib/identity_cache.rb', line 162

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

.should_fill_cache?Boolean

:nodoc:

Returns:

  • (Boolean)


116
117
118
# File 'lib/identity_cache.rb', line 116

def should_fill_cache? # :nodoc:
  !readonly
end

.should_use_cache?Boolean

:nodoc:

Returns:

  • (Boolean)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/identity_cache.rb', line 120

def should_use_cache? # :nodoc:
  ActiveRecord::Base.connection_handler.connection_pool_list(ActiveRecord::Base.current_role).none? do |pool|
    pool.active_connection? &&
      # Rails wraps each of your tests in a transaction, so that any changes
      # made to the database during the test can be rolled back afterwards.
      # These transactions are flagged as "unjoinable", which tries to make
      # your application behave as if they weren't there. In particular:
      #
      #  - Opening another transaction during the test creates a savepoint,
      #    which can be rolled back independently of the main transaction.
      #  - When those nested transactions complete, any `after_commit`
      #    callbacks for records modified during the transaction will run,
      #    even though the changes haven't actually been committed yet.
      #
      # By ignoring unjoinable transactions, IdentityCache's behaviour
      # during your test suite will more closely match production.
      #
      # When there are no open transactions, `current_transaction` returns a
      # special `NullTransaction` object that is unjoinable, meaning we will
      # use the cache.
      pool.connection.current_transaction.joinable?
  end
end

.unmap_cached_nil_for(value) ⇒ Object



166
167
168
# File 'lib/identity_cache.rb', line 166

def unmap_cached_nil_for(value)
  value == IdentityCache::CACHED_NIL ? nil : value
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.



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/identity_cache.rb', line 217

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

  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



234
235
236
237
238
239
240
# File 'lib/identity_cache.rb', line 234

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