Ensure variants are loaded and processed synchronously just once #56225
+24
−1
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Motivation / Background
Today, while looking into some 500 errors from
GET /rails/active_storage/representations/redirect/requests, I noticed an unexpected behaviour in our logs. We were computing a variant for a previewable blob (for example, a video or a PDF, whose variants belong to their preview image), and then, after having uploaded it and stored it, we were trying to load it again from the database, failing to find it, then downloading the preview once more and processing the variant again, and finally failing to create the variant record, because it already existed. Turns out, the reason we couldn't find it the second time we tried to load it was that this was happening in the replica, whereas the new variant is created against the primary, by manually connecting to the writer:rails/activestorage/app/models/active_storage/variant_with_record.rb
Lines 57 to 62 in 17f6e00
This PR avoids loading the record that was created within the request once more just to get the redirect URL. See below for more details.
Detail
Even though we track and memoize the just-created
@recordinActiveStorage::VariantWithRecordwhen we callprocessed(which callsprocess), we always instantiate a new object when we callActiveStorage::Blob#variant.rails/activestorage/app/models/active_storage/blob/representable.rb
Lines 100 to 102 in 17f6e00
If we only call this once, that's fine, but when processing the variant via
GET /rails/active_storage/representations/redirect, we call this twice: one when setting the representation and processing it:rails/activestorage/app/controllers/active_storage/representations/base_controller.rb
Lines 13 to 14 in 17f6e00
and another one when calling
@representation.urlfor the redirect:rails/activestorage/app/controllers/active_storage/representations/redirect_controller.rb
Line 12 in 17f6e00
which calls
ActiveStorage::Preview#url, which ultimately callsActiveStorage::VariantWithRecord.processedagain, but on a new instance ofActiveStorage::VariantWithRecord. This checks whether the variant record exists (the one we just created in the previous call toprocessed), but does so against the replica, which is unlikely to find it if there's any delay, since these calls are very close together.With this change, we make sure we reuse the variant instance we just calculated for the preview image.
Checklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]