You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We utilize a global, in-memory cache with TTL for one of our models per process. We also wanted to use identity_cache so that the whole object tree is fetched with one network call.
However, we receive rare errors when we call fetch_association with the following stack trace:
NameError instance variable @dehydrated_relation not defined
/usr/local/bundle/ruby/3.3.0/gems/identity_cache-1.5.6/lib/identity_cache/cached/recursive/association.rb:34
/usr/local/bundle/ruby/3.3.0/gems/identity_cache-1.5.6/lib/identity_cache/cached/recursive/association.rb:19
Since the code has a guard clause: record.instance_variable_defined?(dehydrated_variable_name), the only way this can happen is that another thread is concurrently executing the same code and has already removed this instance variable.
Are you open to contributions to fix concurrency issues? So far we've only seen this error popping up dozens of times on ~50M requests, however, there might be more that we haven't seen yet or are failing silently 👀
The text was updated successfully, but these errors were encountered:
FYI we use the following monkey-patch to fix this behavior:
# frozen_string_literal: true# Open access to the IdentityCache::Cached moduleIdentityCache.public_constant(:Cached)moduleIdentityCachemoduleCachedmoduleRecursive# Monkey-patch to allow for thread-safe loading of associationsclassAssociationdefinitialize(name,reflection:)super@dehydrated_variable_name=:"@dehydrated_#{name}"@hydration_mutex=Mutex.newenddefread(record)assoc=record.association(name)if !assoc.loaded? && assoc.target.blank? && (record.send(:loaded_by_idc?) || assoc.klass.should_use_cache?)ifrecord.instance_variable_defined?(records_variable_name)record.instance_variable_get(records_variable_name)elsifrecord.instance_variable_defined?(dehydrated_variable_name)hydrate_safely(record,assoc)elseassoc.load_targetendelseassoc.load_targetendendprivate# Synchronizes access to the association with a mutex to prevent removing an ivar twice, which raises an errordefhydrate_safely(record,assoc)@hydration_mutex.synchronizedoifrecord.instance_variable_defined?(records_variable_name)# Another thread may have already hydrated the association - do an early return in this caserecord.instance_variable_get(records_variable_name)elsedehydrated_target=record.instance_variable_get(dehydrated_variable_name)association_target=hydrate_association_target(assoc.klass,dehydrated_target)record.remove_instance_variable(dehydrated_variable_name)set_with_inverse(record,association_target)endendendendendendend
But perhaps it would be easier to keep the dehydration status on the Association itself and get rid of using remove_instance_variable
Hi!
We utilize a global, in-memory cache with TTL for one of our models per process. We also wanted to use
identity_cache
so that the whole object tree is fetched with one network call.However, we receive rare errors when we call
fetch_association
with the following stack trace:which points to this piece of code:
identity_cache/lib/identity_cache/cached/recursive/association.rb
Lines 25 to 42 in 313dfbc
Since the code has a guard clause:
record.instance_variable_defined?(dehydrated_variable_name)
, the only way this can happen is that another thread is concurrently executing the same code and has already removed this instance variable.Are you open to contributions to fix concurrency issues? So far we've only seen this error popping up dozens of times on ~50M requests, however, there might be more that we haven't seen yet or are failing silently 👀
The text was updated successfully, but these errors were encountered: