Rails: Pessimistic Locking
Tags: rails, pessimisticlocking, racecondition
Published: 2019-05-25 07:51:03

---
title: Rails: Pessimistic Locking
published: true
description: How to use Pessimistic locking to avoid pesky race conditions
tags: rails, pessimistic locking, race condition
cover_image: https://images.unsplash.com/photo-1501350699650-d91239ccae6e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3300&q=80
---

Photo cred: [Benjamin Bortels](https://unsplash.com/@ben182)


## Problem

What happens when more then 1 user tries to update a table in our database at the exact same moment? you guessed it ! RACE CONDITION - as a programmer, you must hate those, maybe after reading this we can avoid at least one thing that causes them.


## Definition

#### pessimistic database locking:
- multiple users will not be able to read while others are reading

#### optimistc database locking:
- multiple users can read the same resource at the same time but if more then one tries to modify the database , then we prevent it

In optimistic locking, we only lock it when updating the data. Other requests can still read the subject data. Pessimistic locking, on the other hand, locks all other access to the record. Even the read access is not allowed. With this type of locking, while the first request to the object is updating, all other requests will have to wait for their turn.

In this article we will cover how to handle Pessimistic Locking in rails, which is easier, for optimistic locking check this out: 

[Optimistic Locking](https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.)

## Imagine: 

imagine a like button -

```ruby
  def like(id)
    message = Message.find(id)
    message.like_count += 1
    message.save!
  end
```

if one user press the like button , and another user presses the like button at the same time then instead of the `like_count` of that message going up to 2, it will only increment to 1, because both users pressed increment from 0 to 1 at the same time. 

##`with_lock` 
[Official Docs](https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html)

What with_lock does is a few things. First, it starts a database transaction. Second, it acquires a pessimistic database lock. Once the lock is acquired the record is reloaded in memory so the values on the record match those in the locked database row. The lock will prevent others from reading or writing to that row and anyone else trying to acquire a lock will have to wait for the lock to be released.

```ruby
  def like(id)
    message = Message.find(id)
    message.with_lock do
      message.like_count += 1
      message.save!
    end
  end
```

Now imagine that we want to lock this users account while this lock transaction is taking place , so that no updates can be made to the user model in the scope of this transaction.

what we can do is call `account.lock!`

```ruby
  def like(id)
    message = Message.find(id)
    message.with_lock do
      account.lock!
      message.like_count += 1
      message.save!
    end
  end
```

since we are already in a database transaction (the first lock), we cannot use another `with_lock` block because it itself is a transaction. what we can do inside of this `with_lock` block is call `.lock!` .

This works very similarly to the with_lock method with two exceptions. First, it just acquires the lock at the time of calling and then releases it whenever the surrounding transaction completes rather than managing its own transaction internally. Second, it does absolutely nothing if not called inside of a transaction. __To repeat, don’t use lock! outside of a transaction! Besides that, though, it will ensure the same type of data integrity that the with_lock method does.__

Thanks for reading! Happy locking! 


sources: (thank you!)

https://www.peterdebelak.com/blog/pessimistic-locking-in-rails-by-example/

http://blog.katpadi.ph/race-condition-and-rails-with_lock/