DEV: Redesign chat mentions (#24752)

At the moment, when someone is mentioning a group, or using here or 
all mention, we create a chat_mention record per user. What we want 
instead is to have special kinds of mentions, so we can create only one 
chat_mention record in such cases. This PR implements that.

Note, that such mentions will still have N related notifications, one 
notification per a user. We don't expect we'll have performance 
problems on the notifications side, but if at some point we do, we 
should be able to solve them on the side of notifications 
(notifications are handled in jobs, also some little delays with 
the notifications are acceptable, so we can make sure notifications 
are properly queued, and that processing of every notification is 
fast enough to make delays small enough).

The preparation work for this PR was done in fbd24fa, where we make 
it possible for one mention to have several related notifications.

A pretty tricky part of this PR is schema and data migration, I've explained 
related details inline on the migration files.
This commit is contained in:
Andrei Prigorshnev
2024-01-17 15:24:01 +04:00
committed by GitHub
parent 6876c52857
commit 62f423da15
40 changed files with 613 additions and 157 deletions

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddTypeAndTargetIdToChatMentions < ActiveRecord::Migration[7.0]
def up
add_column :chat_mentions, :type, :string, null: true
add_column :chat_mentions, :target_id, :integer, null: true
change_column_null :chat_mentions, :user_id, true
end
def down
change_column_null :chat_mentions, :user_id, false
remove_column :chat_mentions, :target_id
remove_column :chat_mentions, :type
end
end

View File

@@ -0,0 +1,24 @@
# frozen_string_literal: true
class SetTypeAndTargetIdOnChatMentions < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
BATCH_SIZE = 5000
def up
begin
updated_count = DB.exec(<<~SQL, batch_size: BATCH_SIZE)
WITH cte AS (SELECT id, user_id
FROM chat_mentions
WHERE type IS NULL AND target_id IS NULL
LIMIT :batch_size)
UPDATE chat_mentions
SET type = 'Chat::UserMention', target_id = cte.user_id
FROM cte
WHERE chat_mentions.id = cte.id;
SQL
end while updated_count > 0
end
def down
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
class AddAndRemoveIndexesOnChatMentions < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
def up
remove_index :chat_mentions,
name: :chat_mentions_index,
algorithm: :concurrently,
if_exists: true
add_index :chat_mentions, %i[chat_message_id], algorithm: :concurrently
add_index :chat_mentions, %i[target_id], algorithm: :concurrently
end
def down
remove_index :chat_mentions, %i[target_id], algorithm: :concurrently, if_exists: true
remove_index :chat_mentions, %i[chat_message_id], algorithm: :concurrently, if_exists: true
add_index :chat_mentions,
%i[chat_message_id user_id notification_id],
unique: true,
name: "chat_mentions_index",
algorithm: :concurrently
end
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
class SetTypeAndTargetIdOnChatMentionsPostMigrate < ActiveRecord::Migration[7.0]
disable_ddl_transaction!
BATCH_SIZE = 5000
def up
# we're setting it again in post-migration
# in case some mentions have been created after we run
# this query the first time in the regular migration
begin
updated_count = DB.exec(<<~SQL, batch_size: BATCH_SIZE)
WITH cte AS (SELECT id, user_id
FROM chat_mentions
WHERE type IS NULL AND target_id IS NULL
LIMIT :batch_size)
UPDATE chat_mentions
SET type = 'Chat::UserMention', target_id = cte.user_id
FROM cte
WHERE chat_mentions.id = cte.id;
SQL
end while updated_count > 0
end
def down
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class MakeTypeOnChatMentionsNonNullable < ActiveRecord::Migration[7.0]
def up
change_column_null :chat_mentions, :type, false
end
def down
change_column_null :chat_mentions, :type, true
end
end