Cache boosts with their message instead of one by one

Every boost had its own fragment, nested inside the message fragment that
already holds it. With a cold cache, a read and a write per boost plus a
read_multi and a write_multi per message: a room page of 40 messages with
120 boosts takes ~410 ms instead of ~290 ms. Redis runs without persistence
(config/redis.conf), so every page is cold after a restart.

A boost touches its message, so adding or removing one rewrites the message
fragment anyway. The inner fragments only paid off when a message with many
boosts was drawn again, and there they hid a lookup per booster. The boosters
are now loaded with the boosts, one query per message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142qgjggdJ2KDdGk7RF9Xm9
This commit is contained in:
Marcello Costagliola
2026-10-05 16:40:49 +02:00
parent c73ea37e0c
commit c761763c8c
4 changed files with 60 additions and 19 deletions
+16 -18
View File
@@ -1,20 +1,18 @@
<% cache boost do %>
<div id="<%= dom_id(boost) %>"
class="boost boost-item flex-inline postion--relative max-width align-center fill-white gap"
data-controller="boost-delete" data-boost-delete-perform-class="boost--deleting" data-boost-delete-reveal-class="expanded" data-boost-delete-booster-id-value="<%= boost.booster.id %>">
<figure class="avatar boost__avatar flex-item-no-shrink">
<%= avatar_tag boost.booster, aria: { label: "#{boost.booster.name} boosted #{boost.content}" } %>
</figure>
<div id="<%= dom_id(boost) %>"
class="boost boost-item flex-inline postion--relative max-width align-center fill-white gap"
data-controller="boost-delete" data-boost-delete-perform-class="boost--deleting" data-boost-delete-reveal-class="expanded" data-boost-delete-booster-id-value="<%= boost.booster.id %>">
<figure class="avatar boost__avatar flex-item-no-shrink">
<%= avatar_tag boost.booster, aria: { label: "#{boost.booster.name} boosted #{boost.content}" } %>
</figure>
<%= tag.span boost.content, role: "button",
class: [ "txt-small", { "txt-medium": boost.content.all_emoji? } ],
data: { action: "click->boost-delete#reveal keydown.enter->boost-delete#reveal:prevent", boost_delete_target: "content" } %>
<%= tag.span boost.content, role: "button",
class: [ "txt-small", { "txt-medium": boost.content.all_emoji? } ],
data: { action: "click->boost-delete#reveal keydown.enter->boost-delete#reveal:prevent", boost_delete_target: "content" } %>
<%= button_to message_boost_path(boost.message, boost), method: :delete, data: { action: "boost-delete#perform", boost_delete_target: "button" },
class: "btn btn--negative flex-item-justify-end boost__delete" do %>
<%= image_tag "minus.svg", size: 20, aria: { hidden: "true" } %>
<span class="for-screen-reader">Delete this boost</span>
<% end %>
</div>
<span id="delete_boost_accessible_label" class="for-screen-reader">Press enter to delete this boost</span>
<% end %>
<%= button_to message_boost_path(boost.message, boost), method: :delete, data: { action: "boost-delete#perform", boost_delete_target: "button" },
class: "btn btn--negative flex-item-justify-end boost__delete" do %>
<%= image_tag "minus.svg", size: 20, aria: { hidden: "true" } %>
<span class="for-screen-reader">Delete this boost</span>
<% end %>
</div>
<span id="delete_boost_accessible_label" class="for-screen-reader">Press enter to delete this boost</span>
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="boosts flex flex-wrap align-center gap full-width" style="--column-gap: 0.4ch; --row-gap: 0"
data-controller="turbo-streaming" data-action="turbo:submit-start->turbo-streaming#unsubscribe">
<div class="flex-inline flex-wrap gap" id="<%= dom_id(message, :boosts) %>" data-turbo-streaming-target="container">
<%= render partial: "messages/boosts/boost", collection: message.boosts.ordered, cached: true %>
<%= render partial: "messages/boosts/boost", collection: message.boosts.ordered.includes(:booster) %>
</div>
<%= turbo_frame_tag message, :new_boost do %>