Files
danbooru/db/migrate/20100211181944_create_favorites.rb
evazion 340e1008e9 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.
2021-10-08 21:26:42 -05:00

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