Locking: The Problem Multiple threads/processes trying to update the same DB entity row 1 row 1 row 3 row 1 row 2 Table Client Client do some processing do some processing
Example 1: Counter Video 1 100 views Video 2 200 views Video 3 300 views User 1 User 2 Increment +1 Increment +1 User 1: Find video 1 User 2: Find video 1 User 1: Update video views to 101 User 2: Update video views to 101 1. 2. 3. 4.
Database atomic operation $set - set a particular value $unset - delete a particular field (v1.3+) $inc - increment a particular value by a certain amount $push - append a value to an array $pushAll - append several values to an array $pull - remove a value(s) from an existing array $pullAll - remove several value(s) from an existing array $bit - bitwise operations • • • • • • • Video.find(video_id).inc(:views, 1)
Example 3: Blog Post add update Requires server side logic in between Post text: “Good news, anyone. The Swedish robot from Pi-kea is here with the super collider I ordered.” addUpdate “Bad news, nobody” addUpdate “The super collider exploded” Farnsworth Farnsworth
More specific code for Blog add_update example #### models/post.rb class Post include Mongoid::Document field :text end #### controllers/posts_controller.rb class PostController < ApplicationController ## Adds an "UPDATE: ...some text..." to an existing document def add_update post = Post.find(params[:id]) post.text += "---UPDATE--- " + params[:more_text] post.save end end
Optimistic Locking Fetch the object. Modify the object locally. Send an update request that says "update the object to this new value if it still matches its old value". 1. 2. 3. Post text: “Good news, anyone. The Swedish robot from Pi-kea is here with the super collider I ordered.” version: 1 Farnsworth addUpdate “Bad news, nobody” Post text: “Good news, anyone. The Swedish robot from Pi-kea is here with the super collider I ordered. -- UPDATE -- Bad news, nobody” version: 2 1) Fetch object 2) Concatenate text and increment version 3) Save to DB if version=1 addUpdate “The super collider exploded” Farnsworth 1.5) Fetch object 2.5) Concatenate text and increment version 3.5) Save to DB if version=1 fails ==> retry Post text: “Good news, anyone. The Swedish robot from Pi-kea is here with the super collider I ordered. -- UPDATE -- Bad news, nobody --UPDATE-- The super collider exploded” version: 3 4) Fetch object 5) Concatenate text and increment version 6) Save to DB if version=2
Rails gem for Mongoid mongoid_optimistic_locking #### models/post.rb class Post include Mongoid::Document include Mongoid::OptimisticLocking field :text end #### controllers/posts_controller.rb class PostController < ApplicationController ## Adds an "UPDATE: ...some text..." to an existing document def add_update begin post = Post.find(params[:id]) post.text += "---UPDATE--- " + params[:more_text] post.save rescue Mongoid::Errors::StaleDocument retry end end end