From 32eef8e2eaece3cd55ce597477068dcf6ed2ec88 Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 03:05:14 +0200 Subject: [PATCH] Fix(exchange_rate): Handle RecordInvalid in find_or_fetch_rate race condition (#2734) Co-authored-by: sentry[bot] <39604003+sentry[bot]@users.noreply.github.com> --- app/models/exchange_rate/provided.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models/exchange_rate/provided.rb b/app/models/exchange_rate/provided.rb index e396cfe69..0d96038da 100644 --- a/app/models/exchange_rate/provided.rb +++ b/app/models/exchange_rate/provided.rb @@ -41,10 +41,12 @@ module ExchangeRate::Provided ) do |exchange_rate| exchange_rate.rate = rate.rate end if cache - rescue ActiveRecord::RecordNotUnique - # Race condition: another process inserted between our SELECT and INSERT - # Retry by finding the existing record - ExchangeRate.find_by!( + rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid + # Race condition: another process inserted between our SELECT and INSERT. + # RecordNotUnique = DB unique constraint; RecordInvalid = model uniqueness + # validation fired before the DB got a chance to reject it. Both are safe + # to handle by reading back the record that the other process just saved. + return ExchangeRate.find_by!( from_currency: rate.from, to_currency: rate.to, date: rate.date