Class variables not classy!
(actually)
Class variables caches are not so classy
(umm, in fact)
Class variables are, well, controversial,
class variables used for caching = bad
Class variables bad?
Main Reason
class A
@@x = 2
def say
@@x
end
end
class B < A
def haha=y
@@x=y
end
end
class C < A
def haha=y
@@x=y
end
end
b = B.new
c = C.new
b.say #=> 2
c.say #=> 2
b.haha = 'because I can'
c.say #=> 'because I can'
Other reasons
- Thread safety
- Its a global variable
But still,
they are useful.
Letsbe practical
And my code has no threads !
class A
@@a_variable_shared_across_all_instances_of_A = nil
def my_method
# and it is accessible within the scope
# just like that
@a_variable_shared_across_all_instances_of_A = 'shared-data'
end
end
So whats not classy?
class Shippping
@@countries = Country.all
def label_the_parcel(address)
country = @@countries.detect{|name| name == address.country_name}
print_label country.shipping_code
end
end
So whats not classy?
class Shippping
@@california_code = State.where(country: 'U.S.A').last.shipping_code
def label_for_california(address)
print_label address
print_label @@california_code
end
end
This can fail during class load
This can fail rails s
So whats not classy?
class Shippping
def label_for_california(address)
print_label address
print_label Shipping.california_code
end
def self.california_code
@@california_code ||= State.where(country: 'U.S.A').last.shipping_code
end
end
The instance-variable cache pattern
Because why not
So whats not classy?
class A
def slowly_loaded_data
@slowly_loaded_data ||= slow_queries
end
end
The actual instance-variable cache pattern
So whats not classy?
class Shippping
def label_for_california(address)
print_label address
print_label Shipping.california_code
end
@@california_code = nil
def self.california_code
@@california_code ||= State.where(country: 'U.S.A').last.shipping_code
end
end
That, right there, is a cache
So whats not classy?
And caches have to expire sometime.
Some day, the california_code will change.
So for that day, we have -
So whats not classy?
class Shippping
@@california_code = nil
def self.california_code
@@california_code ||= State.where(country: 'U.S.A').last.shipping_code
end
def self.flush_california_code
@@california_code = nil
end
end
class State
after_save :flush1
def flush1
Shipping.flush_california_cache if name=='california'
end
end
So whats not classy?
And I have no threads
What could possibly go wrong?
So whats not classy?
drum-roll...
Enter Sidekiq
- Cache invalidation will not be carried forward
- The flush1 action will occur only in the web (Unicorn), and not in the Sidekiq
- In fact it will not occur in other unicorn workers too
- It is an in-process cache
What can be done?
- In-process caches can cause subtle bugs
- Using Rails.cache with memcache or File cache (default) is safer
- Instance variable caches are still ok
Who else does it
cd discourse
ag @@ . | wc -l #=>
cd gitlab
ag @@ . | wc -l #=>
cd spree
ag @@ . | wc -l #=>
cd rails
ag @@ . | wc -l #=>
@h6165
Thanks
Class variables not so classy
By Abhishek Yadav
Class variables not so classy
- 1,190