Custom fields concurrency issue

Sometimes we run into concurrency issues with custom fields.

Example is a webhook handler that does this

user_id = params[:externalUserId].split('-')[1]
user = User.find_by(id: user_id)
raise Discourse::NotFound unless user

... do a lot of processing ...

user.custom_fields[:myfield] = params[:whatever]
user.save_custom_fields

When two webhooks fire in rapid succession for the same user, we sometimes end up with a duplicate row. The next time the user custom fields are accessed they look like
{"myfield"=>["value", "value"]}

I see that (user_id, name) is defined as an index but not unique on user_custom_fields.

What would be the best pattern to prevent this from happening?

4 Likes