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

Add Configuration#multi_tenant! for opting into multi-tentant support where configuration is per-thread #556

Merged
merged 2 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@


### Added
* Add `Configuration#multi_tenant!` for opting into multi-tentant support where configuration/caching is per-thread (#556)

### Fixed
* Locked Savon to version `< 2.13` to prevent XML parsing issues.
### Breaking Changes
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,32 @@ NetSuite.configure do
end
```

### Multi-Tenancy

If you're interacting with multiple NetSuite accounts, each in separate threads, you can enable multi-tenancy to prevent your configuration and caches from being shared between threads.

From your main thread, you'd want to enable multi-tenancy:

```ruby
NetSuite.configure do
multi_tentant!
end
```

Note that `multi_tenant!` is a special configuration option which is _not_ effected by `reset!`.

Then in each child thread, you'd perform any configuration specific to the NetSuite account you're interacting with for that thread, all of which will be specific to that thread only:
iloveitaly marked this conversation as resolved.
Show resolved Hide resolved

```ruby
NetSuite.configure do
reset!

account ENV['NETSUITE_ACCOUNT']

# The rest of your usual configuration...
end
```

# Usage

## CRUD Operations
Expand Down
26 changes: 23 additions & 3 deletions lib/netsuite/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ def reset!
end

def attributes
Thread.current[:netsuite_gem_attributes] ||= {}
if multi_tenant?
Thread.current[:netsuite_gem_attributes] ||= {}
else
@attributes ||= {}
end
end

def connection(params={}, credentials={})
Expand Down Expand Up @@ -51,11 +55,19 @@ def filters=(list)
end

def wsdl_cache
Thread.current[:netsuite_gem_wsdl_cache] ||= {}
if multi_tenant?
Thread.current[:netsuite_gem_wsdl_cache] ||= {}
else
@wsdl_cache ||= {}
end
end

def clear_wsdl_cache
Thread.current[:netsuite_gem_wsdl_cache] = {}
if multi_tenant?
Thread.current[:netsuite_gem_wsdl_cache] = {}
else
@wsdl_cache = {}
end
end

def cached_wsdl
Expand Down Expand Up @@ -407,5 +419,13 @@ def proxy(proxy = nil)
attributes[:proxy]
end
end

def multi_tenant!
@multi_tenant = true
end

def multi_tenant?
@multi_tenant
end
end
end
21 changes: 17 additions & 4 deletions lib/netsuite/utilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,31 @@ module Utilities
# TODO need structured logger for various statements

def clear_cache!
Thread.current[:netsuite_gem_netsuite_get_record_cache] = {}
Thread.current[:netsuite_gem_netsuite_find_record_cache] = {}
if NetSuite::Configuration.multi_tenant?
Thread.current[:netsuite_gem_netsuite_get_record_cache] = {}
Thread.current[:netsuite_gem_netsuite_find_record_cache] = {}
else
@netsuite_get_record_cache = {}
@netsuite_find_record_cache = {}
end

DataCenter.clear_cache!
end

def netsuite_get_record_cache
Thread.current[:netsuite_gem_netsuite_get_record_cache] ||= {}
if NetSuite::Configuration.multi_tenant?
Thread.current[:netsuite_gem_netsuite_get_record_cache] ||= {}
else
@netsuite_get_record_cache ||= {}
end
end

def netsuite_find_record_cache
Thread.current[:netsuite_gem_netsuite_find_record_cache] ||= {}
if NetSuite::Configuration.multi_tenant?
Thread.current[:netsuite_gem_netsuite_find_record_cache] ||= {}
else
@netsuite_find_record_cache ||= {}
end
end

def append_memo(ns_record, added_memo, opts = {})
Expand Down
28 changes: 26 additions & 2 deletions spec/netsuite/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,42 @@
expect(config.attributes).to be_empty
end

it 'ensures that attributes are not shared between threads' do
it 'treats attributes as shared/global in default single tenant mode' do
config.attributes[:blah] = 'something'
expect(config.attributes[:blah]).to eq('something')

thread = Thread.new {
expect(config.attributes[:blah]).to eq('something')

config.attributes[:blah] = 'something_else'
expect(config.attributes[:blah]).to eq('something_else')
}

thread.join

expect(config.attributes[:blah]).to eq('something')
expect(config.attributes[:blah]).to eq('something_else')
end

it 'treats attributes as thread-local in multi-tenant mode, which each thread starting with empty attributes' do
begin
config.multi_tenant!

config.attributes[:blah] = 'something'
expect(config.attributes[:blah]).to eq('something')

thread = Thread.new {
expect(config.attributes[:blah]).to be_nil

config.attributes[:blah] = 'something_else'
expect(config.attributes[:blah]).to eq('something_else')
}

thread.join

expect(config.attributes[:blah]).to eq('something')
ensure
config.instance_variable_set(:@multi_tenant, false)
end
end
end

Expand Down