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?
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
.
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
.