Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-1298

Performance challenge with uninitialized references

    • Type: Icon: Task Task
    • Resolution: Done
    • 2.3.2
    • Affects Version/s: None
    • Component/s: None
    • Labels:
      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

            Assignee:
            Unassigned Unassigned
            Reporter:
            reidmo@gmail.com Reid Morrison
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: