Skip to content

Commit 72fb854

Browse files
ClearlyClaireGargron
authored andcommitted
Avoid race condition when streaming deleted statuses (mastodon#10280)
* Avoid race condition when streaming deleted statuses * Move redis lock to DistributionWorker to avoid extra Redis value
1 parent ec85e83 commit 72fb854

2 files changed

Lines changed: 27 additions & 11 deletions

File tree

app/services/remove_status_service.rb

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,22 @@ def call(status, **options)
1414
@stream_entry = status.stream_entry
1515
@options = options
1616

17-
remove_from_self if status.account.local?
18-
remove_from_followers
19-
remove_from_lists
20-
remove_from_affected
21-
remove_reblogs
22-
remove_from_hashtags
23-
remove_from_public
24-
remove_from_media if status.media_attachments.any?
25-
26-
@status.destroy!
17+
RedisLock.acquire(lock_options) do |lock|
18+
if lock.acquired?
19+
remove_from_self if status.account.local?
20+
remove_from_followers
21+
remove_from_lists
22+
remove_from_affected
23+
remove_reblogs
24+
remove_from_hashtags
25+
remove_from_public
26+
remove_from_media if status.media_attachments.any?
27+
28+
@status.destroy!
29+
else
30+
raise Mastodon::RaceConditionError
31+
end
32+
end
2733

2834
# There is no reason to send out Undo activities when the
2935
# cause is that the original object has been removed, since
@@ -156,4 +162,8 @@ def remove_from_media
156162
redis.publish('timeline:public:media', @payload)
157163
redis.publish('timeline:public:local:media', @payload) if @status.local?
158164
end
165+
166+
def lock_options
167+
{ redis: Redis.current, key: "distribute:#{@status.id}" }
168+
end
159169
end

app/workers/distribution_worker.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ class DistributionWorker
44
include Sidekiq::Worker
55

66
def perform(status_id)
7-
FanOutOnWriteService.new.call(Status.find(status_id))
7+
RedisLock.acquire(redis: Redis.current, key: "distribute:#{status_id}") do |lock|
8+
if lock.acquired?
9+
FanOutOnWriteService.new.call(Status.find(status_id))
10+
else
11+
raise Mastodon::RaceConditionError
12+
end
13+
end
814
rescue ActiveRecord::RecordNotFound
915
true
1016
end

0 commit comments

Comments
 (0)