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:
parent
156e04515f
commit
84e13e9b1c
|
@ -1790,14 +1790,17 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def set_status!(description, emoji, ends_at = nil)
|
||||
status = { description: description, emoji: emoji, set_at: Time.zone.now, ends_at: ends_at }
|
||||
|
||||
if user_status
|
||||
user_status.update!(status)
|
||||
else
|
||||
self.user_status = UserStatus.create!(status)
|
||||
end
|
||||
status = {
|
||||
description: description,
|
||||
emoji: emoji,
|
||||
set_at: Time.zone.now,
|
||||
ends_at: ends_at,
|
||||
user_id: id,
|
||||
}
|
||||
validate_status!(status)
|
||||
UserStatus.upsert(status)
|
||||
|
||||
reload_user_status
|
||||
publish_user_status(user_status)
|
||||
end
|
||||
|
||||
|
@ -2175,6 +2178,10 @@ class User < ActiveRecord::Base
|
|||
)
|
||||
SQL
|
||||
end
|
||||
|
||||
def validate_status!(status)
|
||||
UserStatus.new(status).validate!
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
|
Loading…
Reference in New Issue