Skip to content
12 changes: 9 additions & 3 deletions app/models/work.rb
Original file line number Diff line number Diff line change
Expand Up @@ -775,9 +775,15 @@ def number_of_chapters
# Issue 1316: total number needs to reflect the actual number of chapters posted
# rather than the total number of chapters indicated by user
def number_of_posted_chapters
Rails.cache.fetch(key_for_chapter_posted_counting(self)) do
self.chapters.posted.count
end
Rails.cache.fetch(
key_for_chapter_posted_counting(self),
skip_nil: true
) do
count = chapters.posted.count
# Return nil for zero so skip_nil prevents caching stale data.
# The .to_i below converts nil back to 0 for callers.
count.zero? ? nil : count
end.to_i
Comment thread
Bilka2 marked this conversation as resolved.
end

def chapters_in_order(include_drafts: false, include_content: true)
Expand Down
26 changes: 26 additions & 0 deletions spec/models/work_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -598,4 +598,30 @@
end
end
end

describe "#number_of_posted_chapters" do
it "does not cache a zero count from a stale replica" do
work = create(:work)
Rails.cache.clear

stale_relation = double(count: 0)
allow(work).to receive(:chapters).and_return(double(posted: stale_relation))
work.number_of_posted_chapters

allow(work).to receive(:chapters).and_call_original
expect(work.number_of_posted_chapters).to eq(1)
end

it "caches a non-zero count" do
work = create(:work)
Rails.cache.clear

stale_relation = double(count: 3)
allow(work).to receive(:chapters).and_return(double(posted: stale_relation))
work.number_of_posted_chapters

allow(work).to receive(:chapters).and_call_original
expect(work.number_of_posted_chapters).to eq(3)
end
end
end
25 changes: 18 additions & 7 deletions spec/requests/works_n_plus_one_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
describe "n+1 queries in the WorksController" do
include LoginMacros

shared_examples "displaying multiple works efficiently" do
shared_examples "displaying multiple works efficiently" do |queries_per_work: nil|
context "when all works are cached", :n_plus_one do
populate do |n|
WorkIndexer.prepare_for_testing
Expand All @@ -12,11 +12,20 @@
subject.call
end

it "performs a constant number of queries" do
expect do
subject.call
expect(response.body.scan('<li id="work_').size).to eq(current_scale.to_i)
end.to perform_constant_number_of_queries
if queries_per_work
it "performs #{queries_per_work} query per work" do
expect do
subject.call
expect(response.body.scan('<li id="work_').size).to eq(current_scale.to_i)
end.to perform_linear_number_of_queries(slope: queries_per_work)
end
else
it "performs a constant number of queries" do
expect do
subject.call
expect(response.body.scan('<li id="work_').size).to eq(current_scale.to_i)
end.to perform_constant_number_of_queries
end
end
end

Expand Down Expand Up @@ -140,7 +149,9 @@
let!(:work_attributes) { { authors: [user.default_pseud], posted: false } }
let!(:user) { create(:user) }

it_behaves_like "displaying multiple works efficiently"
# Drafts have 0 posted chapters, which we intentionally don't cache
# to avoid persisting stale data after posting.
it_behaves_like "displaying multiple works efficiently", queries_per_work: 1
end

describe "#search" do
Expand Down
Loading