From cff25c186bb8bb7cdd29de8938949c775dc0ec7f Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 16 Feb 2026 15:58:22 +0100 Subject: [PATCH] Fix race condition when processing statuses twice with the same idempotency key (#37879) --- app/services/post_status_service.rb | 31 +++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 3e4a715225a..5b066523fda 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -2,6 +2,7 @@ class PostStatusService < BaseService include Redisable + include Lockable include LanguagesHelper class UnexpectedMentionsError < StandardError @@ -39,19 +40,17 @@ class PostStatusService < BaseService @in_reply_to = @options[:thread] @quoted_status = @options[:quoted_status] - return idempotency_duplicate if idempotency_given? && idempotency_duplicate? + with_idempotency do + validate_media! + preprocess_attributes! - validate_media! - preprocess_attributes! - - if scheduled? - schedule_status! - else - process_status! + if scheduled? + schedule_status! + else + process_status! + end end - redis.setex(idempotency_key, 3_600, @status.id) if idempotency_given? - unless scheduled? postprocess_status! bump_potential_friendship! @@ -208,6 +207,18 @@ class PostStatusService < BaseService @idempotency_duplicate = redis.get(idempotency_key) end + def with_idempotency + return yield unless idempotency_given? + + with_redis_lock("idempotency:lock:status:#{@account.id}:#{@options[:idempotency]}") do + return idempotency_duplicate if idempotency_duplicate? + + yield + + redis.setex(idempotency_key, 3_600, @status.id) + end + end + def scheduled_in_the_past? @scheduled_at.present? && @scheduled_at <= Time.now.utc end