Closed dssjoblom closed 1 year ago
This was introduced in Alchemy 5.1
Please see https://github.com/AlchemyCMS/alchemy_cms/pull/1882/ for why we introduced it.
ActiveStorage does a similar thing. Writing a cache in a GET request is a common thing.
Not sure about the read only replica. Maybe there is a way to restrict those writes on the writing database somehow?
Want to look into it?
@tvdeyen yes, it happens that you sometimes have to write to the database even for a GET request.
The default setup is this: (from https://guides.rubyonrails.org/active_record_multiple_databases.html#activating-automatic-role-switching):
If the application is receiving a POST, PUT, DELETE, or PATCH request the application will automatically write to the writer database. For the specified time after the write, the application will read from the primary. For a GET or HEAD request the application will read from the replica unless there was a recent write.
Switching to the writing role from e.g. a GET can be done like this:
ActiveRecord::Base.connected_to(role: :writing) do
# explicitly write to the primary
end
So this is a pretty simple fix, except I don't know where exactly to put the block in AlchemyCMS. In the application I'm working on, I simply surrounded each alchemy template render with something like this:
<% if AppConfig.cms_blocks_enabled %>
<% alchemy_page = Alchemy::Page.find_by(page_layout: block_name) %>
<% if alchemy_page %>
<div id="<%= id %>" class="<%= classes %>">
<%# for some reason, images can be written during render %>
<% ActiveRecord::Base.connected_to(role: :writing) do %>
<%= render_alchemy_elements from_page: alchemy_page %>
<% end %>
</div>
<% end %>
<% end %>
You can see the same approach in ActiveStorage: https://github.com/rails/rails/blob/75a9e1be75769ae633a938d81d51e06852a69ea3/activestorage/app/models/active_storage/variant_with_record.rb
def create_or_find_record(image:)
@record =
ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role) do
blob.variant_records.create_or_find_by!(variation_digest: variation.digest) do |record|
record.image.attach(image)
end
end
end
Coincidentally, the AS code also uses the create_or_find_by!
idiom to prevent race conditions (#2429).
So this is a pretty simple fix, except I don't know where exactly to put the block in AlchemyCMS. In the application I'm working on, I simply surrounded each alchemy template render with something like this:
Thanks. I think the best place for the ActiveRecord::Base.connected_to(role: :writing)
block would be somewhere around here
https://github.com/AlchemyCMS/alchemy_cms/blob/0e13375c362a60d1e65eec05f4c357a440fddff8/app/models/alchemy/picture/url.rb#L39
You can see the same approach in ActiveStorage: https://github.com/rails/rails/blob/75a9e1be75769ae633a938d81d51e06852a69ea3/activestorage/app/models/active_storage/variant_with_record.rb
Coincidentally, the AS code also uses the create_or_find_by! idiom to prevent race conditions (https://github.com/AlchemyCMS/alchemy_cms/issues/2429).
We are using a similar approach, but maybe the detect
is wrong here and we should explicitly use find_by
as well in order to make sure we read from the database and not an in-memory collection. We used to detect
, because we wanted to avoid database reads in case of an eager loaded collection. But if you think about it, it does not make sense to eager load thumbs, if they are not used anyway (because the elements are cached most of the time)
Will play around a bit.
@dssjoblom would please give
# Gemfile
gem "alchemy_cms", git: "https://github.com/tvdeyen/alchemy_cms", branch: "6.1-write-thumbs-on-writing-db"
a try?
@tvdeyen yes, this seems to work :thumbsup: Thanks a lot!
Steps to reproduce
After upgrading from an older 6.0.0 prerelease version to 6.1.1, it seems like when an image is rendered, it causes database writes:
Expected behavior
I'd expect no database writes on a GET query (the Rails default when using read-only replicas).
Actual behavior
Alchemy apparently tries to create thumbnails (or something similar) while rendering the picture view.
System configuration