-
Type: Task
-
Resolution: Done
-
Affects Version/s: None
-
Component/s: None
-
None
As requested in http://groups.google.com/group/mongoid/browse_thread/thread/f038a761cfac03ab/dd12cfec773a0bcd
If we create a new Mongoid document without saving it, and then
attempt to check any of its has_one relationships to see if it was
set, it results in a Mongoid Database lookup penalty. Also, calling ::save() results in every unassigned relationship performing a lookup before the save completes.
Simplified Example:
module My class Identity include Mongoid::Document belongs_to :person, :class_name => 'My::Person' field :login_id, :type => Integer end class Address include Mongoid::Document embedded_in :person, :class_name => 'My::Person' field :zip, :type => Integer end class Person include Mongoid::Document embeds_many :addresses, :class_name => 'My::Address' has_one :identity, :class_name => 'My::Identity' field :first_name, :type => String field :last_name, :type => String end end p = My::Person.new => #<My::Person _id: 4e79f824f41fd5646d000002, _type: nil, first_name: nil, last_name: nil> # Fetching an unassigned embedded document is fast: Benchmark.ms { p.addresses } => 1.00016593933105 # Fetching an unassigned referenced document is much slower the first time only: Benchmark.ms { p.identity } => 67.9998397827148 # Calling the methods for the second time is almost instantaneous: Benchmark.ms { p.addresses } => 0.0 Benchmark.ms { p.identity } => 0.0
With ActiveRecord, it avoids this penalty since the model has not been
saved yet, so it knows that the referenced models cannot reference
this newly created model.
We have much larger documents with many references that will never
have values, so this has a significant performance penalty when we are
serializing the documents and their referenced documents to JSON. The
serialization is performed prior to when the documents are actually
saved.
Is there any way to avoid the MongoDB lookup for every unassigned
reference?
This performance penalty is also being observed when calling ::save()
p = My::Person.new(:first_name => 'Jack')
Benchmark.ms { p.save }
=> 203.999996185303 ms
Below is the profiling output showing the Identity lookup for the new Person object when Person is saved:
{ "ts" : ISODate("2011-09-28T17:26:58.360Z"), "info" : "query test.system.namespaces reslen:437 nscanned:10 \nquery: {} nreturned:10 bytes:421", "millis" : 0 } { "ts" : ISODate("2011-09-28T17:26:58.376Z"), "info" : "query test.my_identities ntoreturn:1 reslen:36 nscanned:0 \nquery: { $query: { person_id: ObjectId('4e8358e2f41fd51bde000001') }, $orderby: { _id: 1 } } nreturned:0 bytes:20", "millis" : 0 } { "ts" : ISODate("2011-09-28T17:26:58.380Z"), "info" : "query test.system.namespaces reslen:437 nscanned:10 \nquery: {} nreturned:10 bytes:421", "millis" : 0 } { "ts" : ISODate("2011-09-28T17:26:58.410Z"), "info" : "insert test.my_people", "millis" : 0 }
Using Mongoid 2.2.0, JRuby 1.6.4 running Ruby 1.8.7
Any help will be appreciated
Thank you
Reid Morrison