Rails 6 adds new finder method create_or_find_by

May 4, 2019

I have written before about the method find_or_create_by, so what is different about this new addition and why is it needed?

The difference is as the name suggests, in the order of operations. Instead of trying to find a record first and create it if nothing was found, it tries to create it first.

"Wouldn't that always create a record?"

Well, yes, that could be the case if the model does not have constraints to prevent duplicated fields. But if it does, then those constraints will prevent a duplicate record to be inserted and instead fallback to selecting the existing record.

"Okay, so it ends up doing the same thing as findorcreate_by, why do we need another one?"

The reason why this one is needed, is that it addresses a potential race condition.

How does it work?

This might be rare in small to medium sized applications, but if you are looking at a large scaled application, this could become an actual problem. The problem lies within potentially stale reads. Separate threads might attempt to simultaneously SELECT and then INSERT multiple records.

Drawbacks

This new method does not solve all potential race conditions. For example, it could still face a problem when a separate thread deletes a record in between an INSERT that failed and the subsequent SELECT to retrieve the now non-existing record, but that is even rarer than the previous issue so you would have to weigh if it is even worth taking into account.

The more relevant consideration is performance. The database operations and then relying on throwing and catching exceptions in Ruby, tend to be slower.