Read more

PostgreSQL: How to use with_advisory_lock to prevent race conditions

Deleted user #8242
August 04, 2020Software engineer

If you want to prevent that two processes run some code at the same time you can use the gem with_advisory_lock Show archive.org snapshot .

What happens

  1. The thread will wait indefinitely until the lock is acquired.
  2. While inside the block, you will exclusively own the advisory lock.
  3. The lock will be released after your block ends, even if an exception is raised in the block.
Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

This is usually required if there is no suitable database row to lock on.

Example

You want to generate an email address with 2 random numbers in it. The email addresses should be unique. Therefore the random numbers should be regenerated, if the same email address exists already.
To prevent that the same email is generated at the same time, you should lock that code as follows:

def generate_email
  random_numbers = rand(100).to_s.rjust(2, '0')
  email = "#{name}#{random_numbers}@example.de"

  if Customer.where(email: email).present?
    generate_email
  else
    email
  end
end

def set_email
  with_advisory_lock("email for #{name}", transaction: true) do
    self.email = generate_email
  end
end

Note that you need to set the option transaction: true, to remain the lock until the transaction completes. This option is supported by PostgreSQL.

Posted to makandra dev (2020-08-04 16:17)