From 8e275d69770e097588a0a0a4a59f56ed9b50ec7a Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Mon, 3 Aug 2026 14:10:20 +0900 Subject: [PATCH 1/2] proposal: resolve version.item without a query PaperTrail deserializes a changeset through `item`, and its polymorphic `belongs_to` declares `inverse_of: false`, so every version loaded from the association queried for the proposal it already came from. The review screen reads the changeset of every version, so a proposal with 30 revisions issued 32 queries per request. Declaring `inverse_of` on the `has_many` drops that to 2, and cuts the cached render of a 30-revision proposal from 33.2ms to 13.1ms. Co-Authored-By: Claude Opus 5 (1M context) --- app/models/proposal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 890681021..5bc869641 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -95,7 +95,7 @@ class Proposal < ApplicationRecord serialize :last_change, coder: YAML serialize :proposal_data, type: Hash, coder: YAML - has_paper_trail only: %i[title abstract details pitch] + has_paper_trail only: %i[title abstract details pitch], versions: { inverse_of: :item } attr_accessor :updating_user attr_writer :tags, :review_tags From 219a3cfb3004da80af150307b9737ae40edbc79b Mon Sep 17 00:00:00 2001 From: Yusuke Nakamura Date: Mon, 3 Aug 2026 14:10:56 +0900 Subject: [PATCH 2/2] proposal_reviews: cache the rendered diff per version Diffy's html format highlights changes within a line by re-running diff at character granularity, spawning a diff process per changed chunk. That dominates the response: a proposal with 30 revisions took 1.2s to render, and larger histories timed out. Versions are immutable, so the markup can be cached indefinitely. The same proposal now renders in 12.8ms once warm, with the inline highlighting kept as is. Co-Authored-By: Claude Opus 5 (1M context) --- .../staff/proposal_reviews/_reviewer_contents.html.haml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/staff/proposal_reviews/_reviewer_contents.html.haml b/app/views/staff/proposal_reviews/_reviewer_contents.html.haml index dfb81250a..4c1124dc3 100644 --- a/app/views/staff/proposal_reviews/_reviewer_contents.html.haml +++ b/app/views/staff/proposal_reviews/_reviewer_contents.html.haml @@ -28,9 +28,11 @@ - proposal.versions.each.with_index(1) do |version, index| %details.diff-view{ open: index == proposal.versions.size } %summary= version.created_at.to_fs(:day_at_time) - - version.changeset.each do |k, (old, new)| - %h3.control-label= k.titleize - %div= diff old, new + -# Versions are immutable, so the rendered diff can be cached forever. + - cache version do + - version.changeset.each do |k, (old, new)| + %h3.control-label= k.titleize + %div= diff old, new :css = #{Diffy::CSS}