PERF: Keep track of when a users first unread is

This optimisation avoids large scans joining the topics table with the
topic_users table.

Previously when a user carried a lot of read state we would have to join
the entire read state with the topics table. This operation would slow down
home page and every topic page. The more read state you accumulated the
larger the impact.

The optimisation helps people who clean up unread, however if you carry
unread from years ago it will only have minimal impact.
This commit is contained in:
Sam Saffron
2019-04-05 12:44:45 +11:00
parent d299197392
commit 5f896ae8f7
7 changed files with 157 additions and 1 deletions
@@ -0,0 +1,30 @@
class AddFirstUnreadAtToUserStats < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
# so we can rerun this if the index creation fails out of ddl
if !column_exists?(:user_stats, :first_unread_at)
add_column :user_stats, :first_unread_at, :datetime, null: false, default: -> { 'CURRENT_TIMESTAMP' }
end
execute <<~SQL
UPDATE user_stats us
SET first_unread_at = u.created_at
FROM users u
WHERE u.id = us.user_id
SQL
# this is quite a big index to carry, but we need it to optimise home page initial load
# by covering all these columns we are able to quickly retrieve the set of topics that were
# updated in the last N days. We perform a ranged lookup and selectivity may vary a lot
add_index :topics,
[:updated_at, :visible, :highest_staff_post_number, :highest_post_number, :category_id, :created_at, :id],
algorithm: :concurrently,
name: 'index_topics_on_updated_at_public',
where: "(topics.archetype <> 'private_message') AND (topics.deleted_at IS NULL)"
end
def down
raise ActiveRecord::IrreversibleMigration
end
end