-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is Memoist Thread Safe? #45
Comments
@benlieb that depends what you mean by thread-safe. But it depends what objects your memoizing, At the end of the day memoist is mostly just the same as taking def my_method
do_something
end And changing it to def my_method
@__my_method ||= do_something
end If you really wanted to make this "safe" def initialize(*args)
@__my_method_mutex = Mutex.new
end
def my_method
@__my_method_mutex.synchronize do
@__my_method ||= do_something
end
end but that seems crazy Maybe you can write a minimal example of what you're trying to do, |
Basically I want to run my app with Puma on Heroku, which requires "thread-safety". Looking through several articles on the subject, both memoizing and using ||= can be issues when aiming for thread safety. I'm not an expert, but the two articles that I read in-depth were: https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/ I'm not doing anything fancy, but I just want to ensure that all my gems (not picking on you guys :) are thread-safe. In my case the worse case would be one user somehow making a payment with another user's payment profile, as shown above, but here is the code: class User < ActiveRecord::Base
extend Memoist
include UserStripePaymentsModule
...
end
# consolidates payment-realted methods, in this case for Stripe
# try to consider method names that are generic to continue to be used if
# stripe is swapped for a different payment processor in future
module UserStripePaymentsModule
extend ActiveSupport::Concern
included do
memoize :payment_profile # reload and rememoizes with #payment_profile(true)
end
def payment_profile
Stripe::Client.instance.customer_get self
end
def rememoize_payment_profile
payment_profile(true)
end
def has_cc_on_file?
!!cc_info
end
def cc_info
return nil unless has_payment_profile?
payment_profile.sources.data.first
end
def cc_last4
cc_info && cc_info.last4
end
def has_payment_profile?
stripe_customer_id.present?
end
end |
Ok, so to simplify your example, cut memoist out of it. class User < ActiveRecord::Base
def payment_profile
@payment_profile ||= Stripe::Client.instance.customer_get self
end
end So this is memoized for an instance of Two threads running on Puma may do something like this:
But these two What you do need to do is ensure that your classes are loaded at boot But rails loading handles this for you. |
This is a rails app, so the loading issue should be taken care of, or am I wrong? |
@benlieb yeah. |
An example of how you could mess this up. user = User.find 123
5.times do
Thread.new do
5.times do
user.payment_profile
user.payment_profile(true)
end
end
end but that's only because you're using a shared object across threads, |
+1 on having it thread-safe, as in the example provided by @matthewrudy . |
Important RecommendationConsider using MemoWise instead, as it is maintained, fully tested, provides thread safety guarantees, and is much, much faster. Other AlternativesIn case you need a tool with this feature set that is currently maintained, check out: Tip Seriously though, read the important note above. Warning If you must continue - be aware that this is unmaintained software. |
I'm trying to make my app thread-safe to use the Puma server.
I'm using this in my payments code and don't want to get this wrong!
app/models/concerns/user_stripe_payments_module.rb:8: memoize :payment_profile # reload and rememoizes with #payment_profile(true)
app/models/concerns/user_stripe_payments_module.rb:15: def rememoize_payment_profile
The text was updated successfully, but these errors were encountered: