mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Revert unread optimisation, has too many edge cases
This commit is contained in:
parent
4d9481bf47
commit
0aed2533ac
@ -1,9 +0,0 @@
|
|||||||
module Jobs
|
|
||||||
class UpdateFirstTopicUnreadAt < Jobs::Scheduled
|
|
||||||
every 1.day
|
|
||||||
|
|
||||||
def execute(args)
|
|
||||||
UserStat.update_first_topic_unread_at!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -541,10 +541,6 @@ SQL
|
|||||||
def self.reset_highest(topic_id)
|
def self.reset_highest(topic_id)
|
||||||
result = exec_sql "UPDATE topics
|
result = exec_sql "UPDATE topics
|
||||||
SET
|
SET
|
||||||
last_unread_at = (
|
|
||||||
SELECT MAX(created_at) FROM posts
|
|
||||||
WHERE topic_id = :topic_id
|
|
||||||
),
|
|
||||||
highest_staff_post_number = (
|
highest_staff_post_number = (
|
||||||
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
||||||
WHERE topic_id = :topic_id AND
|
WHERE topic_id = :topic_id AND
|
||||||
|
@ -146,19 +146,6 @@ SQL
|
|||||||
message[:notifications_reason_id] = reason_id if reason_id
|
message[:notifications_reason_id] = reason_id if reason_id
|
||||||
MessageBus.publish("/topic/#{topic_id}", message, user_ids: [user_id])
|
MessageBus.publish("/topic/#{topic_id}", message, user_ids: [user_id])
|
||||||
|
|
||||||
if notification_level && notification_level >= notification_levels[:tracking]
|
|
||||||
sql = <<SQL
|
|
||||||
UPDATE user_stats us
|
|
||||||
SET first_topic_unread_at = t.last_unread_at
|
|
||||||
FROM topics t
|
|
||||||
WHERE t.id = :topic_id AND
|
|
||||||
t.last_unread_at < us.first_topic_unread_at AND
|
|
||||||
us.user_id = :user_id
|
|
||||||
SQL
|
|
||||||
exec_sql(sql, topic_id: topic_id, user_id: user_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
DiscourseEvent.trigger(:topic_notification_level_changed,
|
DiscourseEvent.trigger(:topic_notification_level_changed,
|
||||||
notification_level,
|
notification_level,
|
||||||
user_id,
|
user_id,
|
||||||
|
@ -81,30 +81,6 @@ class UserStat < ActiveRecord::Base
|
|||||||
update_columns(reset_bounce_score_after: nil, bounce_score: 0)
|
update_columns(reset_bounce_score_after: nil, bounce_score: 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.update_first_topic_unread_at!
|
|
||||||
|
|
||||||
exec_sql <<SQL
|
|
||||||
UPDATE user_stats us
|
|
||||||
SET first_topic_unread_at = COALESCE(X.first_unread_at, current_timestamp)
|
|
||||||
FROM
|
|
||||||
(
|
|
||||||
SELECT u.id user_id, MIN(last_unread_at) first_unread_at
|
|
||||||
FROM users u
|
|
||||||
JOIN topic_users tu ON tu.user_id = u.id
|
|
||||||
JOIN topics t ON t.id = tu.topic_id
|
|
||||||
WHERE notification_level > 1 AND last_read_post_number < CASE WHEN moderator OR admin
|
|
||||||
THEN t.highest_staff_post_number
|
|
||||||
ELSE t.highest_post_number
|
|
||||||
END
|
|
||||||
AND t.deleted_at IS NULL
|
|
||||||
GROUP BY u.id
|
|
||||||
) X
|
|
||||||
WHERE X.user_id = us.user_id AND X.first_unread_at <> first_topic_unread_at
|
|
||||||
SQL
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def trigger_badges
|
def trigger_badges
|
||||||
|
@ -68,15 +68,28 @@ end
|
|||||||
|
|
||||||
# run this later, cause we need to make sure new application controller resilience is in place first
|
# run this later, cause we need to make sure new application controller resilience is in place first
|
||||||
|
|
||||||
|
ColumnDropper.drop(
|
||||||
|
table: 'user_stats',
|
||||||
|
after_migration: 'DropUnreadTrackingColumns',
|
||||||
|
columns: %w{
|
||||||
|
first_topic_unread_at
|
||||||
|
},
|
||||||
|
on_drop: ->(){
|
||||||
|
STDERR.puts "Removing superflous user stats columns!"
|
||||||
|
ActiveRecord::Base.exec_sql "DROP FUNCTION IF EXISTS first_unread_topic_for(int)"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
ColumnDropper.drop(
|
ColumnDropper.drop(
|
||||||
table: 'topics',
|
table: 'topics',
|
||||||
after_migration: 'AddTopicColumnsBack',
|
after_migration: 'DropUnreadTrackingColumns',
|
||||||
columns: %w{
|
columns: %w{
|
||||||
inappropriate_count
|
inappropriate_count
|
||||||
bookmark_count
|
bookmark_count
|
||||||
off_topic_count
|
off_topic_count
|
||||||
illegal_count
|
illegal_count
|
||||||
notify_user_count
|
notify_user_count
|
||||||
|
last_unread_at
|
||||||
},
|
},
|
||||||
on_drop: ->(){
|
on_drop: ->(){
|
||||||
STDERR.puts "Removing superflous topic columns!"
|
STDERR.puts "Removing superflous topic columns!"
|
||||||
|
@ -1,48 +1,9 @@
|
|||||||
class AddUnreadTrackingColumns < ActiveRecord::Migration
|
class AddUnreadTrackingColumns < ActiveRecord::Migration
|
||||||
def up
|
def up
|
||||||
add_column :user_stats, :first_topic_unread_at, :datetime, null: false, default: "epoch"
|
# no op, no need to create all data, next migration will delete it
|
||||||
add_column :topics, :last_unread_at, :datetime, null: false, default: "epoch"
|
|
||||||
|
|
||||||
execute <<SQL
|
|
||||||
UPDATE topics SET last_unread_at = COALESCE((
|
|
||||||
SELECT MAX(created_at)
|
|
||||||
FROM posts
|
|
||||||
WHERE topics.id = posts.topic_id
|
|
||||||
), current_timestamp)
|
|
||||||
SQL
|
|
||||||
|
|
||||||
execute <<SQL
|
|
||||||
UPDATE user_stats SET first_topic_unread_at = COALESCE((
|
|
||||||
SELECT MIN(last_unread_at) FROM topics
|
|
||||||
JOIN users u ON u.id = user_stats.user_id
|
|
||||||
JOIN topic_users tu ON tu.user_id = user_stats.user_id AND topics.id = tu.topic_id
|
|
||||||
WHERE notification_level > 1 AND last_read_post_number < CASE WHEN moderator OR admin
|
|
||||||
THEN topics.highest_staff_post_number
|
|
||||||
ELSE topics.highest_post_number
|
|
||||||
END
|
|
||||||
AND topics.deleted_at IS NULL
|
|
||||||
), current_timestamp)
|
|
||||||
SQL
|
|
||||||
|
|
||||||
add_index :topics, [:last_unread_at]
|
|
||||||
|
|
||||||
# we need this function for performance reasons
|
|
||||||
execute <<SQL
|
|
||||||
CREATE OR REPLACE FUNCTION first_unread_topic_for(user_id int)
|
|
||||||
RETURNS timestamp AS
|
|
||||||
$$
|
|
||||||
SELECT COALESCE(first_topic_unread_at, 'epoch'::timestamp)
|
|
||||||
FROM users u
|
|
||||||
JOIN user_stats ON user_id = u.id
|
|
||||||
WHERE u.id = $1
|
|
||||||
$$
|
|
||||||
LANGUAGE SQL STABLE
|
|
||||||
SQL
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
execute "DROP FUNCTION first_unread_topic_for(int)"
|
raise "Can not be reverted"
|
||||||
remove_column :user_stats, :first_topic_unread_at
|
|
||||||
remove_column :topics, :last_unread_at
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
class DropUnreadTrackingColumns < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise "Can not be reverted"
|
||||||
|
end
|
||||||
|
end
|
@ -377,17 +377,15 @@ class PostCreator
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update_topic_stats
|
def update_topic_stats
|
||||||
attrs = {last_unread_at: @post.created_at}
|
|
||||||
|
|
||||||
if @post.post_type != Post.types[:whisper]
|
if @post.post_type != Post.types[:whisper]
|
||||||
|
attrs = {}
|
||||||
attrs[:last_posted_at] = @post.created_at
|
attrs[:last_posted_at] = @post.created_at
|
||||||
attrs[:last_post_user_id] = @post.user_id
|
attrs[:last_post_user_id] = @post.user_id
|
||||||
attrs[:word_count] = (@topic.word_count || 0) + @post.word_count
|
attrs[:word_count] = (@topic.word_count || 0) + @post.word_count
|
||||||
attrs[:excerpt] = @post.excerpt(220, strip_links: true) if new_topic?
|
attrs[:excerpt] = @post.excerpt(220, strip_links: true) if new_topic?
|
||||||
attrs[:bumped_at] = @post.created_at unless @post.no_bump
|
attrs[:bumped_at] = @post.created_at unless @post.no_bump
|
||||||
|
@topic.update_attributes(attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
@topic.update_attributes(attrs)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_topic_auto_close
|
def update_topic_auto_close
|
||||||
|
@ -294,7 +294,6 @@ class TopicQuery
|
|||||||
|
|
||||||
list
|
list
|
||||||
.where("tu.last_read_post_number < topics.#{col_name}")
|
.where("tu.last_read_post_number < topics.#{col_name}")
|
||||||
.where("topics.last_unread_at >= first_unread_topic_for(?)", user_id)
|
|
||||||
.where("COALESCE(tu.notification_level, :regular) >= :tracking",
|
.where("COALESCE(tu.notification_level, :regular) >= :tracking",
|
||||||
regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
|
regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
|
||||||
end
|
end
|
||||||
|
@ -411,12 +411,9 @@ describe PostCreator do
|
|||||||
expect(topic.posts_count).to eq(1)
|
expect(topic.posts_count).to eq(1)
|
||||||
expect(topic.highest_staff_post_number).to eq(3)
|
expect(topic.highest_staff_post_number).to eq(3)
|
||||||
|
|
||||||
expect(topic.last_unread_at).to eq(whisper_reply.created_at)
|
|
||||||
|
|
||||||
topic.update_columns(highest_staff_post_number:0,
|
topic.update_columns(highest_staff_post_number:0,
|
||||||
highest_post_number:0,
|
highest_post_number:0,
|
||||||
posts_count: 0,
|
posts_count: 0,
|
||||||
last_unread_at: 1.year.ago,
|
|
||||||
last_posted_at: 1.year.ago)
|
last_posted_at: 1.year.ago)
|
||||||
|
|
||||||
Topic.reset_highest(topic.id)
|
Topic.reset_highest(topic.id)
|
||||||
@ -426,7 +423,6 @@ describe PostCreator do
|
|||||||
expect(topic.posts_count).to eq(1)
|
expect(topic.posts_count).to eq(1)
|
||||||
expect(topic.last_posted_at).to eq(first.created_at)
|
expect(topic.last_posted_at).to eq(first.created_at)
|
||||||
expect(topic.highest_staff_post_number).to eq(3)
|
expect(topic.highest_staff_post_number).to eq(3)
|
||||||
expect(topic.last_unread_at).to eq(whisper_reply.created_at)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,36 +38,6 @@ describe TopicUser do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "first unread" do
|
|
||||||
it "correctly update first unread as needed" do
|
|
||||||
time1 = 3.days.ago
|
|
||||||
time2 = 2.days.ago
|
|
||||||
time3 = 1.days.ago
|
|
||||||
|
|
||||||
topic1 = Fabricate(:topic, last_unread_at: time1)
|
|
||||||
topic2 = Fabricate(:topic, last_unread_at: time2)
|
|
||||||
topic3 = Fabricate(:topic, last_unread_at: time3)
|
|
||||||
|
|
||||||
user = Fabricate(:user)
|
|
||||||
user.user_stat.update_columns(first_topic_unread_at: Time.zone.now)
|
|
||||||
|
|
||||||
TopicUser.change(user.id, topic3, notification_level: tracking)
|
|
||||||
|
|
||||||
user.user_stat.reload
|
|
||||||
expect(user.user_stat.first_topic_unread_at).to be_within(1.second).of(time3)
|
|
||||||
|
|
||||||
TopicUser.change(user.id, topic2, notification_level: watching)
|
|
||||||
|
|
||||||
user.user_stat.reload
|
|
||||||
expect(user.user_stat.first_topic_unread_at).to be_within(1.second).of(time2)
|
|
||||||
|
|
||||||
TopicUser.change(user.id, topic1, notification_level: regular)
|
|
||||||
|
|
||||||
user.user_stat.reload
|
|
||||||
expect(user.user_stat.first_topic_unread_at).to be_within(1.second).of(time2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#notification_levels' do
|
describe '#notification_levels' do
|
||||||
context "verify enum sequence" do
|
context "verify enum sequence" do
|
||||||
before do
|
before do
|
||||||
|
@ -10,34 +10,6 @@ describe UserStat do
|
|||||||
expect(user.user_stat.new_since).to be_present
|
expect(user.user_stat.new_since).to be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#update_first_topic_unread_at" do
|
|
||||||
it "updates date correctly for staff" do
|
|
||||||
now = Time.zone.now
|
|
||||||
|
|
||||||
admin = Fabricate(:admin)
|
|
||||||
topic = Fabricate(:topic,
|
|
||||||
highest_staff_post_number: 7,
|
|
||||||
highest_post_number: 1,
|
|
||||||
last_unread_at: now
|
|
||||||
)
|
|
||||||
|
|
||||||
UserStat.update_first_topic_unread_at!
|
|
||||||
|
|
||||||
admin.reload
|
|
||||||
|
|
||||||
expect(admin.user_stat.first_topic_unread_at).to_not be_within(5.years).of(now)
|
|
||||||
|
|
||||||
TopicUser.change(admin.id, topic.id, last_read_post_number: 1,
|
|
||||||
notification_level: NotificationLevels.all[:tracking])
|
|
||||||
|
|
||||||
UserStat.update_first_topic_unread_at!
|
|
||||||
|
|
||||||
admin.reload
|
|
||||||
|
|
||||||
expect(admin.user_stat.first_topic_unread_at).to be_within(1.second).of(now)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context '#update_view_counts' do
|
context '#update_view_counts' do
|
||||||
|
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
Loading…
Reference in New Issue
Block a user