FIX: keep topic.word_count in sync (#27065)

Whenever one creates, updates, or deletes a post, we should keep the `topic.word_count` counter in sync.

Context - https://meta.discourse.org/t/-/308062
This commit is contained in:
Régis Hanol 2024-05-17 17:05:49 +02:00 committed by GitHub
parent 57eff8b760
commit b908abe35a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 31 deletions

View File

@ -855,14 +855,22 @@ class Topic < ActiveRecord::Base
FROM posts
WHERE deleted_at IS NULL AND post_type <> 4
GROUP BY topic_id
)
),
Z as (
SELECT topic_id,
SUM(COALESCE(posts.word_count, 0)) word_count
FROM posts
WHERE deleted_at IS NULL AND post_type <> 4
GROUP BY topic_id
)
UPDATE topics
SET
highest_staff_post_number = X.highest_post_number,
highest_post_number = Y.highest_post_number,
last_posted_at = Y.last_posted_at,
posts_count = Y.posts_count
FROM X, Y
posts_count = Y.posts_count,
word_count = Z.word_count
FROM X, Y, Z
WHERE
topics.archetype <> 'private_message' AND
X.topic_id = topics.id AND
@ -870,7 +878,8 @@ class Topic < ActiveRecord::Base
topics.highest_staff_post_number <> X.highest_post_number OR
topics.highest_post_number <> Y.highest_post_number OR
topics.last_posted_at <> Y.last_posted_at OR
topics.posts_count <> Y.posts_count
topics.posts_count <> Y.posts_count OR
topics.word_count <> Z.word_count
)
SQL
@ -891,14 +900,22 @@ class Topic < ActiveRecord::Base
FROM posts
WHERE deleted_at IS NULL AND post_type <> 3 AND post_type <> 4
GROUP BY topic_id
),
Z as (
SELECT topic_id,
SUM(COALESCE(posts.word_count, 0)) word_count
FROM posts
WHERE deleted_at IS NULL AND post_type <> 3 AND post_type <> 4
GROUP BY topic_id
)
UPDATE topics
SET
highest_staff_post_number = X.highest_post_number,
highest_post_number = Y.highest_post_number,
last_posted_at = Y.last_posted_at,
posts_count = Y.posts_count
FROM X, Y
posts_count = Y.posts_count,
word_count = Z.word_count
FROM X, Y, Z
WHERE
topics.archetype = 'private_message' AND
X.topic_id = topics.id AND
@ -906,7 +923,8 @@ class Topic < ActiveRecord::Base
topics.highest_staff_post_number <> X.highest_post_number OR
topics.highest_post_number <> Y.highest_post_number OR
topics.last_posted_at <> Y.last_posted_at OR
topics.posts_count <> Y.posts_count
topics.posts_count <> Y.posts_count OR
topics.word_count <> Z.word_count
)
SQL
end
@ -941,6 +959,13 @@ class Topic < ActiveRecord::Base
post_type <> 4
#{post_type}
),
word_count = (
SELECT SUM(COALESCE(posts.word_count, 0)) FROM posts
WHERE topic_id = :topic_id AND
deleted_at IS NULL AND
post_type <> 4
#{post_type}
),
last_posted_at = (
SELECT MAX(created_at) FROM posts
WHERE topic_id = :topic_id AND

View File

@ -287,6 +287,9 @@ class PostRevisor
advance_draft_sequence if !opts[:keep_existing_draft]
end
# bail out if the post or topic failed to save
return false if !successfully_saved_post_and_topic
# Lock the post by default if the appropriate setting is true
if (
SiteSetting.staff_edit_locks_post? && !@post.wiki? && @fields.has_key?("raw") &&
@ -312,16 +315,15 @@ class PostRevisor
# it can fire events in sidekiq before the post is done saving
# leading to corrupt state
QuotedPost.extract_from(@post)
TopicLink.extract_from(@post)
Topic.reset_highest(@topic.id)
post_process_post
update_topic_word_counts
alert_users
publish_changes
grant_badge
TopicLink.extract_from(@post)
ReviewablePost.queue_for_review_if_possible(@post, @editor) if should_create_new_version?
successfully_saved_post_and_topic
@ -714,19 +716,6 @@ class PostRevisor
DiscourseEvent.trigger(:post_edited, @post, self.topic_changed?, self)
end
def update_topic_word_counts
DB.exec(
"UPDATE topics
SET word_count = (
SELECT SUM(COALESCE(posts.word_count, 0))
FROM posts
WHERE posts.topic_id = :topic_id
)
WHERE topics.id = :topic_id",
topic_id: @topic.id,
)
end
def alert_users
return if @editor.id == Discourse::SYSTEM_USER_ID
Jobs.enqueue(:post_alert, post_id: @post.id)

View File

@ -735,6 +735,7 @@ RSpec.describe PostCreator do
highest_staff_post_number: 0,
highest_post_number: 0,
posts_count: 0,
word_count: 0,
last_posted_at: 1.year.ago,
)
@ -743,6 +744,7 @@ RSpec.describe PostCreator do
topic.reload
expect(topic.highest_post_number).to eq(1)
expect(topic.posts_count).to eq(1)
expect(topic.word_count).to eq(5)
expect(topic.last_posted_at).to eq_time(first.created_at)
expect(topic.highest_staff_post_number).to eq(3)
end
@ -1187,10 +1189,10 @@ RSpec.describe PostCreator do
)
end
it "does not increase posts count for small actions" do
it "does not increase posts/words count for small actions" do
topic = Fabricate(:private_message_topic, user: Fabricate(:user, refresh_auto_groups: true))
Fabricate(:post, topic: topic)
p1 = Fabricate(:post, topic: topic)
1.upto(3) do |i|
user = Fabricate(:user)
@ -1200,13 +1202,19 @@ RSpec.describe PostCreator do
expect(topic.posts.where(post_type: Post.types[:small_action]).count).to eq(i)
end
Fabricate(:post, topic: topic)
Topic.reset_highest(topic.id)
expect(topic.reload.posts_count).to eq(2)
expect(topic.word_count).to eq(0)
Fabricate(:post, topic: topic)
p2 = Fabricate(:post, topic: topic)
Topic.reset_highest(topic.id)
topic.reload
expect(topic.posts_count).to eq(2)
expect(topic.word_count).to eq([p1, p2].sum(&:word_count))
p3 = Fabricate(:post, topic: topic)
Topic.reset_all_highest!
expect(topic.reload.posts_count).to eq(3)
topic.reload
expect(topic.posts_count).to eq(3)
expect(topic.word_count).to eq([p1, p2, p3].sum(&:word_count))
end
end

View File

@ -2874,6 +2874,7 @@ RSpec.describe Topic do
topic.reload
expect(topic.posts_count).to eq(1)
expect(topic.word_count).to eq(post1.word_count)
expect(topic.highest_post_number).to eq(post1.post_number)
expect(topic.highest_staff_post_number).to eq(post2.post_number)
expect(topic.last_posted_at).to eq_time(post1.created_at)