Fix account deletion sometimes failing because of optimistic locks (#16317)

* Fix account deletion sometimes failing because of optimistic locks

In some rare occasions[1], deleting accounts would fail with a
`StaleObjectError` exception.

Indeed, account deletion manually sets the `AccountStat` values without
handling cases where the optimistic locking on `AccountStat` would fail.

To my knowledge, with the rewrite of account counters in #15913, the
`DeleteAccountService` is now the only place that changes the counters in
a way that is not atomic.

Since in this specific case, we do not care about the previous values of the
account counters, it appears we don't need locking at all for this table
anymore.

[1]: https://discourse.joinmastodon.org/t/account-cant-be-deleted/3602

* Bump MAX_SUPPORTED_VERSION in maintenance script
pull/1547/head
Claire 2021-06-02 17:41:25 +02:00 committed by GitHub
parent be8079f637
commit 526332c545
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 13 additions and 6 deletions

View File

@ -11,10 +11,11 @@
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# last_status_at :datetime # last_status_at :datetime
# lock_version :integer default(0), not null
# #
class AccountStat < ApplicationRecord class AccountStat < ApplicationRecord
self.locking_column = nil
belongs_to :account, inverse_of: :account_stat belongs_to :account, inverse_of: :account_stat
update_index('accounts#account', :account) update_index('accounts#account', :account)

View File

@ -49,7 +49,6 @@ module AccountCounters
ON CONFLICT (account_id) DO UPDATE ON CONFLICT (account_id) DO UPDATE
SET #{key} = account_stats.#{key} + :value, SET #{key} = account_stats.#{key} + :value,
last_status_at = now(), last_status_at = now(),
lock_version = account_stats.lock_version + 1,
updated_at = now() updated_at = now()
RETURNING id; RETURNING id;
SQL SQL
@ -59,7 +58,6 @@ module AccountCounters
VALUES (:account_id, :default_value, now(), now()) VALUES (:account_id, :default_value, now(), now())
ON CONFLICT (account_id) DO UPDATE ON CONFLICT (account_id) DO UPDATE
SET #{key} = account_stats.#{key} + :value, SET #{key} = account_stats.#{key} + :value,
lock_version = account_stats.lock_version + 1,
updated_at = now() updated_at = now()
RETURNING id; RETURNING id;
SQL SQL

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class RemoveLockVersionFromAccountStats < ActiveRecord::Migration[5.2]
def change
safety_assured do
remove_column :account_stats, :lock_version, :integer, null: false, default: 0
end
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_05_07_001928) do ActiveRecord::Schema.define(version: 2021_05_26_193025) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -111,7 +111,6 @@ ActiveRecord::Schema.define(version: 2021_05_07_001928) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.datetime "last_status_at" t.datetime "last_status_at"
t.integer "lock_version", default: 0, null: false
t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true
end end

View File

@ -14,7 +14,7 @@ module Mastodon
end end
MIN_SUPPORTED_VERSION = 2019_10_01_213028 MIN_SUPPORTED_VERSION = 2019_10_01_213028
MAX_SUPPORTED_VERSION = 2021_05_07_001928 MAX_SUPPORTED_VERSION = 2021_05_26_193025
# Stubs to enjoy ActiveRecord queries while not depending on a particular # Stubs to enjoy ActiveRecord queries while not depending on a particular
# version of the code/database # version of the code/database