Identity Map
Mongoid's identity map is an implementation of the Identity Map Pattern.
Usage
The identity map in Mongoid is a current aid to assist with excessive database queries in relations, and is necessary for eager loading to work.
To enable the identity map, simply change the configuration option in
your mongoid.yml
.
identity_map_enabled: true
You can enable the identity map programatically as well.
Mongoid.identity_map_enabled = true
When a document is now loaded from the database, it is automatically added to the identity map by its class and id. Subsequent request for that document by its id will not hit the database, but rather pull the document back from the identity map itself.
When performing Model.find
queries with the identity map
enabled, Mongoid will automatically look for the document in the
identity map first and return it if it exists before hitting the database.
Band.find(id) #=> Fetch the document from the database. Band.find(id) #=> Gets the document from the identity map. Band.find([ id_one, id_two ]) #=> Fetch the documents from the database. Band.find([ id_one, id_two ]) #=> Gets the documents from the identity map. Band.find([ id_one, id_three ]) #=> Gets the first document from the identity map, the second from the db.
Note the identity map does not store nil
values if the
query to the database did not return any results. So subsequent
finds for the same id will hit the database again. This also includes
eager loading.
The Unit of Work
To prevent database objects from becoming stale, the documents in the identity map should only exist in a single unit of work, which is usually a single request to the application.
Rails
No extra work is needed for Rails users, Mongoid will automatically clear out the identity map after each request.
Sinatra
You can require the Mongoid identity map middleware in your application to clear out the map with each request:
use Rack::Mongoid::Middleware::IdentityMap
Non Rack Based Applications
For users not using rack based apps, you will need to wrap your requests
or application defined unit of work in a Mongoid.unit_of_work
block:
Mongoid.unit_of_work do Person.create(title: "Grand Poobah") end
The unit of work can also be used to temporarily disable the identity map and force documents to load from the database. |
# Disable the identity map for the current thread only. Mongoid.unit_of_work(disable: :current) do Band.find(1) end # Disable the identity map for all threads. Mongoid.unit_of_work(disable: :all) do Band.find(1) end
Rake Tasks and Background Jobs
It is important to note that you should never be using the identity map when executing background jobs, rake tasks, etc. unless you really know that not many documents will be loaded and memory consumption will be low. Otherwise it's a server takedown waiting to happen. In these cases it's best to wrap your task or job in a unit of work.
desc "A long running rake task" task "db:migrate_data" => :environment do Mongoid.unit_of_work(disable: :all) do # Do my work here. end end class BackgroundJob @queue = :background def self.perform(id) Mongoid.unit_of_work(disable: :all) do # Do work here. end end end
Testing
If you have the identity map enabled in your application, you should set up
a global hook to clear out the map before each test so the test suite does
not create memory bloat. For example in RSpec in spec_helper.rb
.
RSpec.configure do |config| config.before(:each) { Mongoid::IdentityMap.clear } end