FIX: Fix a PostgreSQL error when a draft was concurrently created

Moves the new draft creation concurrency handling to PostgreSQL
so the database doesn't error out when the draft is being created
by multiple backends.

Also removes `retry_not_unique` parameter from Draft#set` which is
not called anywhere.

Also fixes a draft update not bumping the `updated_at` column.
This commit is contained in:
Rafael dos Santos Silva 2020-03-16 19:17:20 -03:00
parent f6d6f1701f
commit 226d81fcc5
1 changed files with 22 additions and 19 deletions

View File

@ -7,7 +7,7 @@ class Draft < ActiveRecord::Base
class OutOfSequence < StandardError; end
def self.set(user, key, sequence, data, owner = nil, retry_not_unique: true)
def self.set(user, key, sequence, data, owner = nil)
if SiteSetting.backup_drafts_to_pm_length > 0 && SiteSetting.backup_drafts_to_pm_length < data.length
backup_draft(user, key, sequence, data)
end
@ -59,30 +59,33 @@ class Draft < ActiveRecord::Base
, data = :data
, revisions = revisions + 1
, owner = :owner
, updated_at = CURRENT_TIMESTAMP
WHERE id = :id
SQL
elsif sequence != current_sequence
raise Draft::OutOfSequence
else
begin
Draft.create!(
user_id: user.id,
draft_key: key,
data: data,
sequence: sequence,
owner: owner
)
rescue ActiveRecord::RecordNotUnique => e
# we need this to be fast and with minimal locking, in some cases we can have a race condition
# around 2 controller actions calling for draft creation at the exact same time
# to avoid complex locking and a distributed mutex, since this is so rare, simply add a single retry
if retry_not_unique
set(user, key, sequence, data, owner, retry_not_unique: false)
else
raise e
end
end
opts = {
user_id: user.id,
draft_key: key,
data: data,
sequence: sequence,
owner: owner
}
DB.exec(<<~SQL, opts)
INSERT INTO drafts (user_id, draft_key, data, sequence, owner, created_at, updated_at)
VALUES (:user_id, :draft_key, :data, :sequence, :owner, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT (user_id, draft_key) DO
UPDATE
SET
sequence = :sequence,
data = :data,
revisions = drafts.revisions + 1,
owner = :owner,
updated_at = CURRENT_TIMESTAMP
SQL
end
sequence