Kok A.
Kok A.

Reputation: 177

What's the better redis locking mechanism to use to avoid async validation fails in rails?

I encountered a problem wherein asynchronous saving in one of our models with uniqueness validation declared in Application level is not blocked by the said validation. Adding the validation in the Database level is not an option for me due to a toggleable option on my app to enable/disable the uniqueness validation.

While searching I saw that Redis lock is what's best for this kind of case, but since I am new to the term of "Redis lock" I need a bit of advice on which plugin/implementation is better to achieve my needed behaviour.

I saw one of the responses that could be similar to my problem here Ruby - Redis based mutex with expiration implementation, and it says that a plugin is not needed since it could be achieved with Redis alone like this

def lock(key, timeout)
  if @redis.set(key, Time.now, nx: true, px: timeout)
    begin
      yield
    ensure
      release key
    end
  end
end

And I also saw that some recommend this redlock plugin found here https://github.com/leandromoreira/redlock-rb but could be an overkill.

Thanks in advance

Upvotes: 1

Views: 1719

Answers (1)

Pascal
Pascal

Reputation: 8646

You have several possibilities:

Serialize the save operation

Assuming you use some sort of background job: drop those jobs in a queue where there is just one worker.

Lock the save operation

Don't enter the save operation while the lock is held. Could be done via Redis and Redlocker (don't do it on your own, see the comment and details about Redlocker here: https://redis.io/commands/set)

But both don't solve this problem: what will you do when there are two records with and one of them can nit be saved? Is this handled or can it be ignored?

Note that the uniqueness validation of Rails is NOT transactional and you can not rely on it to enforce uniqueness (basically it is a select before the insert/update). See https://phraseapp.com/blog/posts/pitfalls-uniqueness-validation-for-uniqueness-using-rails-activerecord/

The only place to properly enforce uniqueness is in the DB. I consider validations good for reporting errors to a user but when it comes to integrity of your data you should have this secured in the DB.

The "toggleable option" does not seem to make much sense to me. Once the uniqueness check is disabled, duplicates will end up in the DB. Enabling the uniqueness check will not change this for existing data.

Perhaps you can add a partial index? (https://www.postgresql.org/docs/current/indexes-partial.html)

CREATE UNIQUE INDEX unique ON orders (some_column) WHERE (some_othercolumn is null);

(assuming Postgres)

Upvotes: 2

Related Questions