Skip to content
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

Fix non thread safe gateway initialization #3216

Conversation

bitberry-dev
Copy link

Hi!

Noticed non thread safe code here

ActiveMerchant::Billing::Base.mode = gateway_options[:server].to_sym

It can cause problems with payments. And this kind of setup should be used only in initializers.

Instead of this I instantiate gateway object with test option based on :server and :test_mode.

btw, :test_mode option has no effect in active merchant gateways.

test_server = gateway_options[:server] != 'production'
test_mode = gateway_options[:test_mode]

gateway_options[:test] = (test_server or test_mode)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style/AndOr: Use || instead of or.

@bitberry-dev bitberry-dev force-pushed the bufgix/fix_non_thread_safe_gateway_initialization branch from aa59438 to 5ff0656 Compare May 30, 2019 14:18
test_mode = gateway_options[:test_mode]

gateway_options[:test] = (test_server || test_mode)

@gateway ||= gateway_class.new(gateway_options)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current implementation is not thread safe, but ||= operator isn't thread safe neither: https://tenderlovemaking.com/2011/10/20/connection-management-in-activerecord.html

We should use a mutex in order to make it thread safe: https://lucaguidi.com/2014/03/27/thread-safety-with-ruby/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, the question here is not in ||= operator used to set instance variable :)) but that you can't change the class variable (global) and expect that the gateway instance will use exactly this value. In next call this class variable will be overwritten and all gateway instances will use it, not one from their preference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I got it now :) my apologies!

test_mode = gateway_options[:test_mode]

gateway_options[:test] = (test_server || test_mode)

@gateway ||= gateway_class.new(gateway_options)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I got it now :) my apologies!

@spaghetticode
Copy link
Member

@bitberryru I think the changes make sense, thank you! 👏

For reference and understandability, as the changes deal with the activemerchant gem codebase, I'm cut & pasting the relevant code from the currently bundled version:

# from lib/active_merchant/billing/gateway.rb

# Initialize a new gateway.
#
# See the documentation for the gateway you will be using to make sure there are no other
# required options.
def initialize(options = {})
  @options = options
end

# Are we running in test mode?
def test?
  (@options.has_key?(:test) ? @options[:test] : Base.test?)
end

Copy link
Member

@kennyadsl kennyadsl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bitberryru thanks for your PR! 🎉

I also noticed we are doing something similar here but I'm not sure we are actually using that class anywhere, maybe we can deprecate it... 🤔

Correct me if I'm wrong but I think that in terms of backward compatibility, this is not going to break any store since, if now "test mode" is set by using the name of the :server preference, we can say working stores have all the gateways configured with the :server preference set to production. If that's true, they will continue working correctly, right?

@bitberry-dev
Copy link
Author

bitberry-dev commented Jun 10, 2019

@kennyadsl Yes, it should not break any store, but when updating it is still worth checking the settings of payment methods :) Only payment methods where server preference set to production and test_mode turned off will work in "production" mode.

I also noticed we are doing something similar here but I'm not sure we are actually using that class anywhere, maybe we can deprecate it... 🤔

Wow, I didn't see it before and never use 😃 This code must also be fixed, if you are not going to delete this class, I can fix it.

@bitberry-dev bitberry-dev force-pushed the bufgix/fix_non_thread_safe_gateway_initialization branch from 5ff0656 to 57095de Compare June 10, 2019 12:04
@bitberry-dev
Copy link
Author

Spree::BillingIntegration also fixed.

@bitberry-dev bitberry-dev force-pushed the bufgix/fix_non_thread_safe_gateway_initialization branch from 57095de to e6cc021 Compare June 10, 2019 12:05
@kennyadsl kennyadsl merged commit ae543e7 into solidusio:master Jun 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants