FIX: avoid race condition when setting user status (#19817)

We caught it in logs, race condition led to this error:

    ActiveRecord::RecordNotUnique 
    (PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "user_statuses_pkey"
    DETAIL:  Key (user_id)=(15) already exists.)


The reason the problem happened was that we were checking if a user has status and if not inserting status:

    if user_status
      ...
    else
      self.user_status = UserStatus.create!(status)
    end

The problem is that it's possible that another request will insert status just after we check if status exists and just before our request call `UserStatus.create!(status)`. Using `upsert` fixes the problem because under the hood `upsert` generates the only SQL request that uses "INSERT ... ON CONFLICT DO UPDATE". So we do everything in one SQL query, and that query takes care of resolving possible conflicts.
This commit is contained in:
Andrei Prigorshnev 2023-02-06 18:56:28 +04:00 committed by GitHub
parent 156e04515f
commit 84e13e9b1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1790,14 +1790,17 @@ class User < ActiveRecord::Base
end end
def set_status!(description, emoji, ends_at = nil) def set_status!(description, emoji, ends_at = nil)
status = { description: description, emoji: emoji, set_at: Time.zone.now, ends_at: ends_at } status = {
description: description,
if user_status emoji: emoji,
user_status.update!(status) set_at: Time.zone.now,
else ends_at: ends_at,
self.user_status = UserStatus.create!(status) user_id: id,
end }
validate_status!(status)
UserStatus.upsert(status)
reload_user_status
publish_user_status(user_status) publish_user_status(user_status)
end end
@ -2175,6 +2178,10 @@ class User < ActiveRecord::Base
) )
SQL SQL
end end
def validate_status!(status)
UserStatus.new(status).validate!
end
end end
# == Schema Information # == Schema Information