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.
61 lines
1.4 KiB
Ruby
61 lines
1.4 KiB
Ruby
class CreateFavorites < ActiveRecord::Migration[4.2]
|
|
TABLE_COUNT = 100
|
|
|
|
def self.up
|
|
# this is a dummy table and should not be used
|
|
create_table "favorites" do |t|
|
|
t.column :user_id, :integer
|
|
t.column :post_id, :integer
|
|
end
|
|
|
|
0.upto(TABLE_COUNT - 1) do |i|
|
|
execute <<-EOS
|
|
create table favorites_#{i} (
|
|
check (user_id % 100 = #{i})
|
|
) inherits (favorites)
|
|
EOS
|
|
|
|
add_index "favorites_#{i}", :user_id
|
|
add_index "favorites_#{i}", :post_id
|
|
end
|
|
|
|
fragment = []
|
|
|
|
1.upto(TABLE_COUNT - 1) do |i|
|
|
fragment << <<-EOS
|
|
elsif (NEW.user_id % 100 = #{i}) then
|
|
insert into favorites_#{i} values (NEW.*);
|
|
EOS
|
|
end
|
|
|
|
execute <<-EOS
|
|
create or replace function favorites_insert_trigger()
|
|
returns trigger as $$
|
|
begin
|
|
if (NEW.user_id % 100 = 0) then
|
|
insert into favorites_0 values (NEW.*);
|
|
#{fragment.join("\n")}
|
|
end if;
|
|
return NULL;
|
|
end;
|
|
$$
|
|
language plpgsql
|
|
EOS
|
|
|
|
execute <<-EOS
|
|
create trigger insert_favorites_trigger
|
|
before insert on favorites
|
|
for each row execute procedure favorites_insert_trigger()
|
|
EOS
|
|
end
|
|
|
|
def self.down
|
|
0.upto(TABLE_COUNT - 1) do |i|
|
|
drop_table "favorites_#{i}"
|
|
end
|
|
|
|
drop_table "favorites"
|
|
execute "DROP FUNCTION favorites_insert_trigger"
|
|
end
|
|
end
|