Strong consistency for a Rails model in Mongoid 3
Asked Answered
F

2

6

I want all of my db interactions for a specific model to go through the mongo primary in my cluster, so I set the model to use strong consistency.

class Photo
  include Mongoid::Document
  with consistency: :strong

  field :number, type: Integer
  # let's say a photo number is unique in the db
  validate :unique_number
end

But this does not seem to work, because I still run into validation errors when I save two Photo photos very close together.

photo1 # db has number=1 for this object
photo1.update_attributes(number: 2)
photo2.number = 1
photo2.save! # <= this raises a validation exception

My understanding of strong consistency is that there shouldn't be a race here. It should do the write and then do the read, and since it's all off the primary there shouldn't be a conflict. What am I missing?

Fireweed answered 8/10, 2015 at 4:27 Comment(4)
Do you have multiple web servers or is this a single server?Airedale
@GavinMiller multiple web servers and a sharded and multiply-replicated mongo cluster.Fireweed
Are you sure this Exception comes from Mongo and not from Mongoid itself? I suspect Mongoid to Raise a ValidationException due to checking it's internal caches and not with the actual Database. If I recall correctly update_attributes does not update all the internal caches of Mongoid at all times.Martial
@Martial the stacktrace finishes in mongoid github.com/mongodb/mongoid/blob/v3.1.7/lib/mongoid/… I don't know if it goes any deeper than that or what the behavior is to generate the error value.Fireweed
F
0

It turns out calling with(consistency: :strong) at the class level only applies it to the next query. So the class method is called when the class is loaded, setting strong consistency for the first query, but subsequent queries don't trigger the same class method leaving their persistence operations to operate with eventual consistency. From the Mongoid 3.1.7 documentation:

Tell the next persistance [sic] operation to store in a specific collection, database or session.

This method does not enforce the persistence options that can be passed in (like a few other methods in the class), so we can also pass in consistency: :strong.

Hack Fix

In order to apply this to every* persistence operation, I added it to a default_scope.

class App
  default_scope -> { with(consistency: :strong); where({}) }
end

In this case, the default scope expects to have a Mongoid Criteria object returned, so we return a noop where clause after setting the consistency level on the in-progress persistence operation.

* This will not be applied if the developer decides to call unscoped and strip off the default_scope.

Fireweed answered 9/8, 2017 at 6:33 Comment(0)
I
0

What you area experiencing looks like it is persistence. The update_attributes is making an atomic change on the document, and it looks like it is not updating the persisted photo1.Your photo2 validation is fired from within the persistence (i.e. on the rails server and not in mongo) and is looking at the records it has. If you ran photo1.reload after the photo1.update_attributes this may sort this for you.

It's been a while since I used mongoid 3, 4 has been the staple for a while and recently upgraded to 5.You won't find this type of issue in mongoid 4.

If the reload does not help, please output photo2.errors so I can pin point the issue for you.

Ingressive answered 16/11, 2015 at 8:27 Comment(0)
F
0

It turns out calling with(consistency: :strong) at the class level only applies it to the next query. So the class method is called when the class is loaded, setting strong consistency for the first query, but subsequent queries don't trigger the same class method leaving their persistence operations to operate with eventual consistency. From the Mongoid 3.1.7 documentation:

Tell the next persistance [sic] operation to store in a specific collection, database or session.

This method does not enforce the persistence options that can be passed in (like a few other methods in the class), so we can also pass in consistency: :strong.

Hack Fix

In order to apply this to every* persistence operation, I added it to a default_scope.

class App
  default_scope -> { with(consistency: :strong); where({}) }
end

In this case, the default scope expects to have a Mongoid Criteria object returned, so we return a noop where clause after setting the consistency level on the in-progress persistence operation.

* This will not be applied if the developer decides to call unscoped and strip off the default_scope.

Fireweed answered 9/8, 2017 at 6:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.