PERF: Use `MessageBus.last_ids` instead of `MessageBus.last_id` for chat (#19523)

Prior to this change, each request executed 2 Redis calls per chat channel
that was loaded. The number of Redis calls quickly adds up once a user
is following multiple channels.
This commit is contained in:
Alan Guo Xiang Tan 2022-12-20 08:20:45 +08:00 committed by GitHub
parent bd5f57e90c
commit 14d54872f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 19 deletions

View File

@ -24,6 +24,8 @@ class ChatChannelSerializer < ApplicationSerializer
def initialize(object, opts)
super(object, opts)
@opts = opts
@current_user_membership = opts[:membership]
end
@ -98,8 +100,8 @@ class ChatChannelSerializer < ApplicationSerializer
def message_bus_last_ids
{
new_messages: MessageBus.last_id("/chat/#{object.id}/new-messages"),
new_mentions: MessageBus.last_id("/chat/#{object.id}/new-mentions"),
new_messages: @opts[:new_messages_message_bus_last_id] || MessageBus.last_id(ChatPublisher.new_messages_message_bus_channel(object.id)),
new_mentions: @opts[:new_mentions_message_bus_last_id] || MessageBus.last_id(ChatPublisher.new_mentions_message_bus_channel(object.id)),
}
end

View File

@ -10,6 +10,8 @@ class StructuredChannelSerializer < ApplicationSerializer
root: nil,
scope: scope,
membership: channel_membership(channel.id),
new_messages_message_bus_last_id: chat_message_bus_last_ids[ChatPublisher.new_messages_message_bus_channel(channel.id)],
new_mentions_message_bus_last_id: chat_message_bus_last_ids[ChatPublisher.new_mentions_message_bus_channel(channel.id)]
)
end
end
@ -21,6 +23,8 @@ class StructuredChannelSerializer < ApplicationSerializer
root: nil,
scope: scope,
membership: channel_membership(channel.id),
new_messages_message_bus_last_id: chat_message_bus_last_ids[ChatPublisher.new_messages_message_bus_channel(channel.id)],
new_mentions_message_bus_last_id: chat_message_bus_last_ids[ChatPublisher.new_mentions_message_bus_channel(channel.id)]
)
end
end
@ -31,17 +35,46 @@ class StructuredChannelSerializer < ApplicationSerializer
end
def message_bus_last_ids
last_ids = {
channel_metadata: MessageBus.last_id("/chat/channel-metadata"),
channel_edits: MessageBus.last_id("/chat/channel-edits"),
channel_status: MessageBus.last_id("/chat/channel-status"),
new_channel: MessageBus.last_id("/chat/new-channel"),
ids = {
channel_metadata: chat_message_bus_last_ids[ChatPublisher::CHANNEL_METADATA_MESSAGE_BUS_CHANNEL],
channel_edits: chat_message_bus_last_ids[ChatPublisher::CHANNEL_EDITS_MESSAGE_BUS_CHANNEL],
channel_status: chat_message_bus_last_ids[ChatPublisher::CHANNEL_STATUS_MESSAGE_BUS_CHANNEL],
new_channel: chat_message_bus_last_ids[ChatPublisher::NEW_CHANNEL_MESSAGE_BUS_CHANNEL]
}
if !scope.anonymous?
last_ids[:user_tracking_state] = MessageBus.last_id(
"/chat/user-tracking-state/#{scope.user.id}",
)
if id = chat_message_bus_last_ids[ChatPublisher.user_tracking_state_message_bus_channel(scope.user.id)]
ids[:user_tracking_state] = id
end
ids
end
private
def chat_message_bus_last_ids
@chat_message_bus_last_ids ||= begin
message_bus_channels = [
ChatPublisher::CHANNEL_METADATA_MESSAGE_BUS_CHANNEL,
ChatPublisher::CHANNEL_EDITS_MESSAGE_BUS_CHANNEL,
ChatPublisher::CHANNEL_STATUS_MESSAGE_BUS_CHANNEL,
ChatPublisher::NEW_CHANNEL_MESSAGE_BUS_CHANNEL,
]
if !scope.anonymous?
message_bus_channels.push(ChatPublisher.user_tracking_state_message_bus_channel(scope.user.id))
end
object[:public_channels].each do |channel|
message_bus_channels.push(ChatPublisher.new_messages_message_bus_channel(channel.id))
message_bus_channels.push(ChatPublisher.new_mentions_message_bus_channel(channel.id))
end
object[:direct_message_channels].each do |channel|
message_bus_channels.push(ChatPublisher.new_messages_message_bus_channel(channel.id))
message_bus_channels.push(ChatPublisher.new_mentions_message_bus_channel(channel.id))
end
MessageBus.last_ids(*message_bus_channels)
end
last_ids
end
end

View File

@ -1,6 +1,10 @@
# frozen_string_literal: true
module ChatPublisher
def self.new_messages_message_bus_channel(chat_channel_id)
"/chat/#{chat_channel_id}/new-messages"
end
def self.publish_new!(chat_channel, chat_message, staged_id)
content =
ChatMessageSerializer.new(
@ -10,9 +14,11 @@ module ChatPublisher
content[:type] = :sent
content[:stagedId] = staged_id
permissions = permissions(chat_channel)
MessageBus.publish("/chat/#{chat_channel.id}", content.as_json, permissions)
MessageBus.publish(
"/chat/#{chat_channel.id}/new-messages",
self.new_messages_message_bus_channel(chat_channel.id),
{
message_id: chat_message.id,
user_id: chat_message.user.id,
@ -120,22 +126,32 @@ module ChatPublisher
)
end
def self.user_tracking_state_message_bus_channel(user_id)
"/chat/user-tracking-state/#{user_id}"
end
def self.publish_user_tracking_state(user, chat_channel_id, chat_message_id)
MessageBus.publish(
"/chat/user-tracking-state/#{user.id}",
self.user_tracking_state_message_bus_channel(user.id),
{ chat_channel_id: chat_channel_id, chat_message_id: chat_message_id.to_i }.as_json,
user_ids: [user.id],
)
end
def self.new_mentions_message_bus_channel(chat_channel_id)
"/chat/#{chat_channel_id}/new-mentions"
end
def self.publish_new_mention(user_id, chat_channel_id, chat_message_id)
MessageBus.publish(
"/chat/#{chat_channel_id}/new-mentions",
self.new_mentions_message_bus_channel(chat_channel_id),
{ message_id: chat_message_id }.as_json,
user_ids: [user_id],
)
end
NEW_CHANNEL_MESSAGE_BUS_CHANNEL = "/chat/new-channel"
def self.publish_new_channel(chat_channel, users)
users.each do |user|
serialized_channel =
@ -145,7 +161,7 @@ module ChatPublisher
root: :chat_channel,
membership: chat_channel.membership_for(user),
).as_json
MessageBus.publish("/chat/new-channel", serialized_channel, user_ids: [user.id])
MessageBus.publish(NEW_CHANNEL_MESSAGE_BUS_CHANNEL, serialized_channel, user_ids: [user.id])
end
end
@ -171,9 +187,11 @@ module ChatPublisher
)
end
CHANNEL_EDITS_MESSAGE_BUS_CHANNEL = "/chat/channel-edits"
def self.publish_chat_channel_edit(chat_channel, acting_user)
MessageBus.publish(
"/chat/channel-edits",
CHANNEL_EDITS_MESSAGE_BUS_CHANNEL,
{
chat_channel_id: chat_channel.id,
name: chat_channel.title(acting_user),
@ -183,17 +201,21 @@ module ChatPublisher
)
end
CHANNEL_STATUS_MESSAGE_BUS_CHANNEL = "/chat/channel-status"
def self.publish_channel_status(chat_channel)
MessageBus.publish(
"/chat/channel-status",
CHANNEL_STATUS_MESSAGE_BUS_CHANNEL,
{ chat_channel_id: chat_channel.id, status: chat_channel.status },
permissions(chat_channel),
)
end
CHANNEL_METADATA_MESSAGE_BUS_CHANNEL = "/chat/channel-metadata"
def self.publish_chat_channel_metadata(chat_channel)
MessageBus.publish(
"/chat/channel-metadata",
CHANNEL_METADATA_MESSAGE_BUS_CHANNEL,
{ chat_channel_id: chat_channel.id, memberships_count: chat_channel.user_count },
permissions(chat_channel),
)