Retrospring/db/migrate/20200704163504_use_timestam...

55 lines
1.9 KiB
Ruby

# frozen_string_literal: true
require "securerandom"
# This migration changes the IDs of several tables from serial to a
# timestamped/"snowflake" one.
#
# Instead of "snowflakes" we shall call those IDs Cornflakes or Waspflakes
# instead.
#
# Implementation somewhat lifted from Mastodon.
class UseTimestampedIds < ActiveRecord::Migration[5.2]
def up
# PL/pgSQL is just spicy pascal
# don't @ me
execute <<~SQL
CREATE or replace FUNCTION gen_timestamp_id(tblname text) RETURNS bigint AS $$
DECLARE
timepart bigint;
seqpart bigint;
BEGIN
timepart := (date_part('epoch', now()) * 1000)::bigint << 16;
seqpart := ('x' || substr(md5(tblname ||
'#{SecureRandom.hex(16)}' ||
timepart::text), 1, 4))::bit(16)::bigint;
RETURN timepart | ((seqpart + nextval(tblname || '_id_seq')) & 65535);
END;
$$ LANGUAGE plpgsql VOLATILE;
SQL
# we need to migrate related columns to bigints for this to work
{
question: %i[answers inboxes],
answer: %i[comments smiles subscriptions],
comment: %i[comment_smiles],
user: %i[announcements answers comment_smiles comments inboxes list_members lists moderation_comments moderation_votes questions reports services smiles subscriptions themes users_roles],
# polymorphic tables go brrr
recipient: %i[notifications],
source: %i[relationships],
target: %i[notifications relationships reports],
}.each do |ref, tbls|
tbls.each do |tbl|
say "Migrating #{tbl}.#{ref}_id to bigint"
change_column(tbl, :"#{ref}_id", :bigint)
end
end
%i[questions answers comments smiles comment_smiles users].each do |tbl|
say "Migrating #{tbl} to use timestamped ids"
change_column(tbl, :id, :bigint, default: -> { "gen_timestamp_id('#{tbl}'::text)" })
end
end
end