appsignal

Upgrading to Mongoid 5 / mongo-ruby-driver

Robert Beekman

Robert Beekman on

Development configuration

One of the first things you'll encounter when starting your Rails app after upgrading to Mongoid 5 is an error about your database config being incorrect.

The fix is easy, just change sessions to clients:

yaml
development: clients: default: database: appsignal_development hosts: - localhost:27017

Driver changes

In our codebase we "drop down to the driver" a lot to execute queries directly on moped/mongo-ruby-driver, instead of using Mongoid, e.g. to create collections for each account. Here you'll also need to change session to client. Another change is that read now expects a hash with a :mode key, instead of the value directly:

ruby
def create_log_entry_collection Mongoid .client('default') # used to be `.session('default')` .with(:read => {:mode => :primary}) # used to be `read: => :primary` .database .command(:create => 'foo') end

Moped has an insert method that accepts either a single document or an array of documents. The new mongo-ruby-driver comes with two separate methods, and you should pick one depending on the amount of documents you'd like to insert:

ruby
# Before Mongoid.client('default')['foo'].insert(document) Mongoid.client('default')['foo'].insert([document, document]) # After Mongoid.client('default')['foo'].insert_one(document) Mongoid.client('default')['foo'].insert_many([document, document])

Lack of ordering

One of the biggest changes with the new driver is that documents are no longer ordered on _id by default.

First and last no longer add an _id sort when no sorting options have been provided. In order to guarantee that a document is the first or last, it needs to now contain an explicit sort.

This means that anywhere where you rely on ordering (.first, .last) you need to explicitly order the query by _id:

ruby
# Before expect( User.first.name ).to eq 'bob' expect( User.last.name ).to eq 'kelso' # After expect( User.asc('_id').first.name ).to eq 'bob' expect( User.asc('_id').last.name ).to eq 'kelso'

In order to make sure our code behaves as it used to, we created a concern that adds a default scope that orders by _id:

ruby
# concerns/ordered_by_id_asc.rb module OrderedByIdAsc extend ActiveSupport::Concern included do default_scope -> { asc('_id') } end end
ruby
# models/account.rb class Account include Mongoid::Document include Mongoid::Timestamps include OrderedByIdAsc end

FindAndModify

Find_and_modify has been removed. Instead you now have 3 methods to chose from:

  • find_one_and_update
  • find_one_and_replace (Convenience method, calls find_one_and_update)
  • find_one_and_delete

ExpireAfterSeconds

One of the more obscure changes is the way a TTL index is created. We use TTL indexes to automatically purge customer data depending on their plan (e.g. after 7 days, or after a month).

The option on the index used to be called expire_after_seconds, but has been renamed to expire_after:

ruby
# Before collection.indexes.create_one( {:time => 1}, {:expire_after_seconds => ttl} ) # After: collection.indexes.create_one( {:time => 1}, {:expire_after => ttl} )

Staging/Production config changes

While in development we only needed to change sessions to clients, but our staging/production configs needed a lot more work:

yaml
# Before staging: sessions: default: database: appsignal_main username: <%= ENV['MONGOID_USERNAME'] %> password: <%= ENV['MONGOID_PASSWORD'] %> hosts: - mongo1.staging:27017 - mongo2.staging:27017 - mongo3.staging:27017 options: read: :primary pool_size: {{ mongoid_pool_size }} ssl: ca_file: /etc/ssl/certs/root_ca.crt client_cert: /app/shared/config/mongodb_app.crt client_key: /app/shared/config/mongodb_app.key # After staging: clients: default: database: appsignal_main hosts: - mongo1.staging:27017 - mongo2.staging:27017 - mongo3.staging:27017 options: user: <%= ENV['MONGOID_USERNAME'] %> password: <%= ENV['MONGOID_PASSWORD'] %> read: mode: :primary max_pool_size: {{ mongoid_pool_size }} ssl: true ssl_ca_cert: /etc/ssl/certs/root_ca.crt ssl_cert: /app/shared/config/mongodb_app.crt ssl_key: /app/shared/config/mongodb_app.key replica_set: staging
  • username has been renamed to user and moved to options
  • password has been moved to options
  • read now expects a nested key named mode
  • SSL is no longer a nested hash, but is set under options
  • The config requires a replica_set key if the setup is a replicaset

The upgrade documentation says that MongoDB 2.4 and 2.6 use :plain auth, but we needed to remove the auth_mech key all together for the setup to work.

Conclusion

Although this is quite an extensive list, we found the upgrade to be relatively painless and the new driver feels a lot more solid than the old Moped driver.

Robert Beekman

Robert Beekman

As a co-founder, Robert wrote our very first commit. He's also our support role-model and knows all about the tiny details in code. Travels and photographs (at the same time).

All articles by Robert Beekman

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps