Performance challenge with uninitialized references

24 views
Skip to first unread message

Reid Morrison

unread,
Sep 21, 2011, 3:32:32 PM9/21/11
to Mongoid
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.

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?

Using Mongoid 2.2.0, JRuby 1.6.4 running Ruby 1.8.7

Any help will be appreciated

Thank you
Reid Morrison

Durran Jordan

unread,
Sep 21, 2011, 3:40:05 PM9/21/11
to mon...@googlegroups.com
If you open a github issue for it I can make the optimization for you - that's not a hard thing to check in our case. But right now there isn't a way to avoid it.

2011/9/21 Reid Morrison <rei...@gmail.com>

Reid Morrison

unread,
Sep 28, 2011, 8:23:25 PM9/28/11
to Mongoid
Oops the previous patch stopped all documents from being saved

How about this one:

module Mongoid
module Relations
module Accessors

def build(name, object, metadata, options = {})
loading = options[:loading]
relation = persisted? || !loading ? create_relation(object,
metadata, loading) : nil
set_relation(name, relation)
end

end
end
end

This one also passes all the Mongoid tests.

Reid Morrison

unread,
Sep 28, 2011, 7:36:26 PM9/28/11
to Mongoid
Created issue https://github.com/mongoid/mongoid/issues/1297

I would like to help with this fix, but I am new to the Mongoid
internals, so I debugged the code a few times and came up with the
following patch:

module Mongoid
module Relations
module Accessors

def build(name, object, metadata, options = {})
relation = (options[:loading] == true) && persisted? ?
create_relation(object, metadata, options[:loading]) : nil
set_relation(name, relation)
end

end
end
end

Does this make sense? Or is it more suitable to put it somewhere else?

The original build method looks like:

def build(name, object, metadata, options = {})
relation = create_relation(object, metadata,
options[:loading])
set_relation(name, relation)
end

On Sep 21, 11:40 am, Durran Jordan <dur...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages