Hello @Ilya_Starovoytov, thank you for sharing your results and some code to reproduce them. This is very helpful.

I’ve taken the code you provided and put it in a single script—I believe it all does the same thing yours does, and I do see a performance regression going from 7.5.4 to 8.1.4. However, in my case, it’s only about 6x slower, rather than ~100x slower.

This is still significant, and we intend to investigate it, but I want to make sure that I’m investigating the same behavior you’re seeing. Can you take the script I’m using (copied below) and confirm the same performance you reported?

require 'mongoid'
require 'factory_bot'
require 'rspec/autorun'

Mongoid.connect_to 'mongoid-5740'
Mongoid.purge!

class First
  include Mongoid::Document
  include Mongoid::Timestamps::Short

  field :name, type: String

  has_many :seconds, dependent: :destroy
  has_many :thirds, dependent: :destroy

  validates_uniqueness_of :name
  validates_presence_of :name

end

class Second
  include Mongoid::Document
  include Mongoid::Timestamps::Short

  field :name, type: String

  belongs_to :first
  has_and_belongs_to_many :thirds
end

class Third
  include Mongoid::Document
  include Mongoid::Timestamps::Short

  field :name, type: String

  validates_presence_of :name
  validates_format_of :name, without: /\/|\\|\&|\?|\s/

  has_and_belongs_to_many :seconds
  belongs_to :first
end

FactoryBot.define do
  factory :first do
    sequence(:name){ |i| "First_name_#{i}" }
  end
end

FactoryBot.define do
  factory :second do
    sequence(:name){ |i| "Second_name_#{i}" }
  end
 end

FactoryBot.define do
  factory :third do
    sequence(:name){ |i| "Third_name_#{i}" }
  end
end

RSpec.describe 'insert performance' do
  include FactoryBot::Syntax::Methods

  let(:first) { create(:first) }
  let(:second_1) { create(:second, first: first) }
  let(:second_2) { create(:second, first: first) }
  let(:thirds_1000) { create_list(:third, 1000, first: first, seconds: [second_1, second_2]) }

  it 'checks 1000 thirds insertion' do
    start_time = Time.now.utc
    thirds_1000
    puts "[#{Mongoid::VERSION}]: #{Time.now.utc - start_time} seconds"
  end
end

When I run this against 7.5.4, 8.0.7, 8.1.4, and the master branch (9.0.0-alpha), I get the following timings (on my M1 Macbook, against MongoDB 7.0.5 running locally in a sharded configuration):

[7.5.4]: 33.691952 seconds
[8.0.7]: 186.871009 seconds
[8.1.4]: 186.438292 seconds
[9.0.0.alpha]: 173.630884 seconds

The performance regression I’m seeing here seems to be related to belongs_to associations enforcing validations all the way up the association chain, on save. When I set Mongoid.belongs_to_required_by_default = false and re-run the test, I get the following timings:

[8.0.7]: 31.389694 seconds
[8.1.4]: 29.274998 seconds
[9.0.0.alpha]: 35.607121 seconds

Which, as you can see, puts the results back into the same ballpark as 7.5.4. We are currently tracking this particular regression in the following tickets:

I’m hoping that you’re encountering this same regression, rather than something new, but please let me know what you see when you run this script. Thank you again!

  • Jamis