Skip to content

Commit

Permalink
Backports to 9.0 (redux) (#5887)
Browse files Browse the repository at this point in the history
* MONGOID-5815 Respect client_override for new docs (#5880)

* MONGOID-5805 Short-circuit the logic in extract_attribute to fix performance regression (backport to 8.1-stable) (#5869)

* short-circuit the logic in extract_attribute to fix performance regression

* short circuit on anything that's not a document or hash

* handle hash/string keys

---------

Co-authored-by: Dmitry Rybakov <[email protected]>
  • Loading branch information
jamis and comandeo-mongo authored Oct 21, 2024
1 parent fc09714 commit a1f1bbc
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 2 deletions.
15 changes: 14 additions & 1 deletion lib/mongoid/clients/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def persistence_context
else
PersistenceContext.get(self) ||
PersistenceContext.get(self.class) ||
PersistenceContext.new(self.class, storage_options)
PersistenceContext.new(self.class, default_storage_options)
end
end

Expand All @@ -112,6 +112,19 @@ def persistence_context?

private

def default_storage_options
# Nothing is overridden, we use either the default storage_options
# or storage_options defined for the document class.
return storage_options if Threaded.client_override.nil? && Threaded.database_override.nil?

storage_options.tap do |opts|
# Globally overridden client replaces client defined for the document class.
opts[:client] = Threaded.client_override unless Threaded.client_override.nil?
# Globally overridden database replaces database defined for the document class.
opts[:database] = Threaded.database_override unless Threaded.database_override.nil?
end
end

def set_persistence_context(options_or_context)
PersistenceContext.set(self, options_or_context)
end
Expand Down
16 changes: 15 additions & 1 deletion lib/mongoid/matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,25 @@ module Matcher
# from and behaves identically to association traversal for the purposes
# of, for example, subsequent array element retrieval.
#
# @param [ Document | Hash ] document The document to extract from.
# @param [ Document | Hash | String ] document The document to extract from.
# @param [ String ] key The key path to extract.
#
# @return [ Object | Array ] Field value or values.
module_function def extract_attribute(document, key)
# The matcher system will wind up sending atomic values to this as well,
# when attepting to match more complex types. If anything other than a
# Document or a Hash is given, we'll short-circuit the logic and just
# return an empty array.
return [] unless document.is_a?(Hash) || document.is_a?(Document)

# Performance optimization; if the key does not include a '.' character,
# it must reference an immediate attribute of the document.
unless key.include?('.')
hash = document.respond_to?(:attributes) ? document.attributes : document
key = find_exact_key(hash, key)
return key ? [ hash[key] ] : []
end

if document.respond_to?(:as_attributes, true)
# If a document has hash fields, as_attributes would keep those fields
# as Hash instances which do not offer indifferent access.
Expand Down
4 changes: 4 additions & 0 deletions spec/mongoid/association/referenced/belongs_to/proxy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,10 @@
person.save!
end

# NOTE: there as a bad interdependency here, with the auto_save_spec.rb
# file. If auto_save_spec.rb runs before this, the following specs fail
# with "undefined method `nullify' for an instance of Person".

context "when parent exists" do

context "when child is destroyed" do
Expand Down
125 changes: 125 additions & 0 deletions spec/mongoid/clients/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -522,4 +522,129 @@
end
end
end

context 'with global overrides' do
let(:default_subscriber) do
Mrss::EventSubscriber.new
end

let(:override_subscriber) do
Mrss::EventSubscriber.new
end

context 'when global client is overridden' do
before do
Mongoid.clients['override_client'] = { hosts: SpecConfig.instance.addresses, database: 'default_override_database' }
Mongoid.override_client('override_client')
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.client('override_client').subscribe(Mongo::Monitoring::COMMAND, override_subscriber)
end

after do
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.client('override_client').unsubscribe(Mongo::Monitoring::COMMAND, override_subscriber)
Mongoid.override_client(nil)
Mongoid.clients['override_client'] = nil
end

it 'uses the overridden client for create' do
Minim.create!

expect(override_subscriber.single_command_started_event('insert').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('insert')).to be_empty
end

it 'uses the overridden client for queries' do
Minim.where(name: 'Dmitry').to_a

expect(override_subscriber.single_command_started_event('find').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('find')).to be_empty
end

context 'when the client is set on the model level' do
let(:model_level_subscriber) do
Mrss::EventSubscriber.new
end

around(:example) do |example|
opts = Minim.storage_options
Minim.storage_options = Minim.storage_options.merge( { client: 'model_level_client' } )
Mongoid.clients['model_level_client'] = { hosts: SpecConfig.instance.addresses, database: 'model_level_database' }
Mongoid.client('model_level_client').subscribe(Mongo::Monitoring::COMMAND, override_subscriber)
example.run
Mongoid.client('model_level_client').unsubscribe(Mongo::Monitoring::COMMAND, override_subscriber)
Mongoid.clients['model_level_client'] = nil
Minim.storage_options = opts
end

# This behaviour is consistent with 8.x
it 'uses the overridden client for create' do
Minim.create!

expect(override_subscriber.single_command_started_event('insert').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('insert')).to be_empty
expect(model_level_subscriber.command_started_events('insert')).to be_empty
end

# This behaviour is consistent with 8.x
it 'uses the overridden client for queries' do
Minim.where(name: 'Dmitry').to_a

expect(override_subscriber.single_command_started_event('find').database_name).to eq('default_override_database')
expect(default_subscriber.command_started_events('find')).to be_empty
expect(model_level_subscriber.command_started_events('find')).to be_empty
end
end
end

context 'when global database is overridden' do
before do
Mongoid.override_database('override_database')
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
end

after do
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.override_database(nil)
end

it 'uses the overridden database for create' do
Minim.create!

expect(default_subscriber.single_command_started_event('insert').database_name).to eq('override_database')
end

it 'uses the overridden database for queries' do
Minim.where(name: 'Dmitry').to_a

expect(default_subscriber.single_command_started_event('find').database_name).to eq('override_database')
end

context 'when the database is set on the model level' do
around(:example) do |example|
opts = Minim.storage_options
Minim.storage_options = Minim.storage_options.merge( { database: 'model_level_database' } )
Mongoid.clients['model_level_client'] = { hosts: SpecConfig.instance.addresses, database: 'model_level_database' }
Mongoid.client(:default).subscribe(Mongo::Monitoring::COMMAND, default_subscriber)
example.run
Mongoid.client(:default).unsubscribe(Mongo::Monitoring::COMMAND, default_subscriber)
Mongoid.clients['model_level_client'] = nil
Minim.storage_options = opts
end

# This behaviour is consistent with 8.x
it 'uses the overridden database for create' do
Minim.create!

expect(default_subscriber.single_command_started_event('insert').database_name).to eq('override_database')
end

it 'uses the overridden database for queries' do
Minim.where(name: 'Dmitry').to_a

expect(default_subscriber.single_command_started_event('find').database_name).to eq('override_database')
end
end
end
end
end

0 comments on commit a1f1bbc

Please sign in to comment.