favorites: merge favorites subtables.
Merge the 100 favorite subtables into a single table. Previously the favorites table was partitioned by user id into 100 subtables to try to make searching by user id faster. This wasn't really necessary and probably slower than just making an index on (favorites.user_id, favorites.id) to satisfy ordfav searches. BTree indexes are logarithmic so dividing an index by 100 doesn't make it 100 times faster to search; instead it just removes a layer or two from the tree. This also adds a uniqueness index on (user_id, post_id) to prevent duplicate favorites. Previously we had to check for duplicates at the application layer, which required careful locking to do it correctly. Finally, this adds an index on favorites.id, which was surprisingly missing before. This made ordering and deleting favorites by id really slow because it degraded to a sequential scan.
This commit is contained in:
@@ -431,8 +431,7 @@ class PostQueryBuilder
|
||||
favuser = User.find_by_name(username)
|
||||
|
||||
if favuser.present? && Pundit.policy!(current_user, favuser).can_see_favorites?
|
||||
favorites = Favorite.from("favorites_#{favuser.id % 100} AS favorites").where(user: favuser)
|
||||
Post.where(id: favorites.select(:post_id))
|
||||
Post.where(id: favuser.favorites.select(:post_id))
|
||||
else
|
||||
Post.none
|
||||
end
|
||||
@@ -509,6 +508,8 @@ class PostQueryBuilder
|
||||
relation = search_order(relation, "created_at_desc")
|
||||
elsif find_metatag(:order) == "custom"
|
||||
relation = search_order_custom(relation, select_metatags(:id).map(&:value))
|
||||
elsif has_metatag?(:ordfav)
|
||||
# no-op
|
||||
else
|
||||
relation = search_order(relation, find_metatag(:order))
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ class Favorite < ApplicationRecord
|
||||
belongs_to :post
|
||||
belongs_to :user
|
||||
|
||||
scope :for_user, ->(user_id) { where("favorites.user_id % 100 = ? AND favorites.user_id = ?", user_id.to_i % 100, user_id) }
|
||||
scope :for_user, ->(user_id) { where(user_id: user_id) }
|
||||
scope :public_favorites, -> { where(user: User.bit_prefs_match(:enable_private_favorites, false)) }
|
||||
|
||||
def self.visible(user)
|
||||
|
||||
@@ -138,7 +138,7 @@ class User < ApplicationRecord
|
||||
has_many :forum_posts, -> {order("forum_posts.created_at, forum_posts.id")}, :foreign_key => "creator_id"
|
||||
has_many :user_name_change_requests, -> {order("user_name_change_requests.created_at desc")}
|
||||
has_many :favorite_groups, -> {order(name: :asc)}, foreign_key: :creator_id
|
||||
has_many :favorites, ->(rec) {where("user_id % 100 = #{rec.id % 100} and user_id = #{rec.id}").order("id desc")}
|
||||
has_many :favorites
|
||||
has_many :ip_bans, foreign_key: :creator_id
|
||||
has_many :tag_aliases, foreign_key: :creator_id
|
||||
has_many :tag_implications, foreign_key: :creator_id
|
||||
|
||||
@@ -50,10 +50,11 @@ class CreateFavorites < ActiveRecord::Migration[4.2]
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table "favorites"
|
||||
|
||||
0.upto(TABLE_COUNT - 1) do |i|
|
||||
drop_table "favorites_#{i}"
|
||||
end
|
||||
|
||||
drop_table "favorites"
|
||||
execute "DROP FUNCTION favorites_insert_trigger"
|
||||
end
|
||||
end
|
||||
|
||||
31
db/migrate/20211008091234_merge_favorites_tables.rb
Normal file
31
db/migrate/20211008091234_merge_favorites_tables.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require_relative "20100211181944_create_favorites.rb"
|
||||
|
||||
class MergeFavoritesTables < ActiveRecord::Migration[6.1]
|
||||
def up
|
||||
execute "set statement_timeout = 0"
|
||||
|
||||
execute "CREATE TABLE favorites_copy AS SELECT id, user_id, post_id FROM favorites"
|
||||
revert CreateFavorites
|
||||
rename_table :favorites_copy, :favorites
|
||||
|
||||
add_index :favorites, [:user_id, :post_id], unique: true, if_not_exists: true
|
||||
add_index :favorites, [:user_id, :id], if_not_exists: true
|
||||
add_index :favorites, :post_id, if_not_exists: true
|
||||
change_column_null :favorites, :user_id, false
|
||||
change_column_null :favorites, :post_id, false
|
||||
|
||||
execute "ALTER TABLE favorites ADD PRIMARY KEY (id)"
|
||||
max_id = Favorite.maximum(:id).to_i
|
||||
execute "CREATE SEQUENCE IF NOT EXISTS favorites_id_seq START #{max_id+1} OWNED BY favorites.id"
|
||||
execute "ALTER TABLE favorites ALTER COLUMN id SET DEFAULT nextval('favorites_id_seq')"
|
||||
end
|
||||
|
||||
def down
|
||||
execute "set statement_timeout = 0"
|
||||
|
||||
rename_table :favorites, :favorites_copy
|
||||
run CreateFavorites
|
||||
execute "INSERT INTO favorites(id, user_id, post_id) SELECT id, user_id, post_id FROM favorites_copy"
|
||||
drop_table :favorites_copy
|
||||
end
|
||||
end
|
||||
3435
db/structure.sql
3435
db/structure.sql
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user