mirror of
https://github.com/mastodon/mastodon.git
synced 2025-02-06 06:55:04 +00:00
Optimize timeline generation (#33839)
Some checks are pending
Bundler Audit / security (push) Waiting to run
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Haml Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / Libvips tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.2) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Some checks are pending
Bundler Audit / security (push) Waiting to run
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Haml Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / Libvips tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.2) (push) Blocked by required conditions
Ruby Testing / Libvips tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
This commit is contained in:
parent
4af91e564d
commit
1be5269151
|
@ -42,7 +42,7 @@ class FeedManager
|
||||||
when :home
|
when :home
|
||||||
filter_from_home(status, receiver.id, build_crutches(receiver.id, [status]), :home)
|
filter_from_home(status, receiver.id, build_crutches(receiver.id, [status]), :home)
|
||||||
when :list
|
when :list
|
||||||
(filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status]), :list)
|
(filter_from_list?(status, receiver) ? :filter : nil) || filter_from_home(status, receiver.account_id, build_crutches(receiver.account_id, [status], list: list), :list)
|
||||||
when :mentions
|
when :mentions
|
||||||
filter_from_mentions?(status, receiver.id) ? :filter : nil
|
filter_from_mentions?(status, receiver.id) ? :filter : nil
|
||||||
when :tags
|
when :tags
|
||||||
|
@ -121,7 +121,7 @@ class FeedManager
|
||||||
|
|
||||||
timeline_key = key(:home, into_account.id)
|
timeline_key = key(:home, into_account.id)
|
||||||
aggregate = into_account.user&.aggregates_reblogs?
|
aggregate = into_account.user&.aggregates_reblogs?
|
||||||
query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
query = from_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
||||||
|
|
||||||
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
||||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
||||||
|
@ -149,7 +149,7 @@ class FeedManager
|
||||||
|
|
||||||
timeline_key = key(:list, list.id)
|
timeline_key = key(:list, list.id)
|
||||||
aggregate = list.account.user&.aggregates_reblogs?
|
aggregate = list.account.user&.aggregates_reblogs?
|
||||||
query = from_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
query = from_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
|
||||||
|
|
||||||
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
|
||||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
||||||
|
@ -157,10 +157,10 @@ class FeedManager
|
||||||
end
|
end
|
||||||
|
|
||||||
statuses = query.to_a
|
statuses = query.to_a
|
||||||
crutches = build_crutches(list.account_id, statuses)
|
crutches = build_crutches(list.account_id, statuses, list: list)
|
||||||
|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
next if filter_from_home(status, list.account_id, crutches) || filter_from_list?(status, list)
|
next if filter_from_home(status, list.account_id, crutches, :list)
|
||||||
|
|
||||||
add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate)
|
add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate)
|
||||||
end
|
end
|
||||||
|
@ -272,23 +272,32 @@ class FeedManager
|
||||||
limit = FeedManager::MAX_ITEMS / 2
|
limit = FeedManager::MAX_ITEMS / 2
|
||||||
aggregate = account.user&.aggregates_reblogs?
|
aggregate = account.user&.aggregates_reblogs?
|
||||||
timeline_key = key(:home, account.id)
|
timeline_key = key(:home, account.id)
|
||||||
|
over_limit = false
|
||||||
|
|
||||||
account.statuses.limit(limit).each do |status|
|
account.statuses.limit(limit).each do |status|
|
||||||
add_to_feed(:home, account.id, status, aggregate_reblogs: aggregate)
|
add_to_feed(:home, account.id, status, aggregate_reblogs: aggregate)
|
||||||
end
|
end
|
||||||
|
|
||||||
account.following.includes(:account_stat).reorder(nil).find_each do |target_account|
|
account.following.includes(:account_stat).reorder(nil).find_each do |target_account|
|
||||||
if redis.zcard(timeline_key) >= limit
|
query = target_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(limit)
|
||||||
|
|
||||||
|
over_limit ||= redis.zcard(timeline_key) >= limit
|
||||||
|
if over_limit
|
||||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
||||||
last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at)
|
last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at, with_random: false)
|
||||||
|
|
||||||
# If the feed is full and this account has not posted more recently
|
# If the feed is full and this account has not posted more recently
|
||||||
# than the last item on the feed, then we can skip the whole account
|
# than the last item on the feed, then we can skip the whole account
|
||||||
# because none of its statuses would stay on the feed anyway
|
# because none of its statuses would stay on the feed anyway
|
||||||
next if last_status_score < oldest_home_score
|
next if last_status_score < oldest_home_score
|
||||||
|
|
||||||
|
# No need to get older statuses
|
||||||
|
query = query.where(id: oldest_home_score...)
|
||||||
end
|
end
|
||||||
|
|
||||||
statuses = target_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, :account, reblog: :account).limit(limit)
|
statuses = query.to_a
|
||||||
|
next if statuses.empty?
|
||||||
|
|
||||||
crutches = build_crutches(account.id, statuses)
|
crutches = build_crutches(account.id, statuses)
|
||||||
|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
|
@ -308,23 +317,32 @@ class FeedManager
|
||||||
limit = FeedManager::MAX_ITEMS / 2
|
limit = FeedManager::MAX_ITEMS / 2
|
||||||
aggregate = list.account.user&.aggregates_reblogs?
|
aggregate = list.account.user&.aggregates_reblogs?
|
||||||
timeline_key = key(:list, list.id)
|
timeline_key = key(:list, list.id)
|
||||||
|
over_limit = false
|
||||||
|
|
||||||
list.active_accounts.includes(:account_stat).reorder(nil).find_each do |target_account|
|
list.active_accounts.includes(:account_stat).reorder(nil).find_each do |target_account|
|
||||||
if redis.zcard(timeline_key) >= limit
|
query = target_account.statuses.list_eligible_visibility.includes(reblog: :account).limit(limit)
|
||||||
|
|
||||||
|
over_limit ||= redis.zcard(timeline_key) >= limit
|
||||||
|
if over_limit
|
||||||
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
|
||||||
last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at)
|
last_status_score = Mastodon::Snowflake.id_at(target_account.last_status_at, with_random: false)
|
||||||
|
|
||||||
# If the feed is full and this account has not posted more recently
|
# If the feed is full and this account has not posted more recently
|
||||||
# than the last item on the feed, then we can skip the whole account
|
# than the last item on the feed, then we can skip the whole account
|
||||||
# because none of its statuses would stay on the feed anyway
|
# because none of its statuses would stay on the feed anyway
|
||||||
next if last_status_score < oldest_home_score
|
next if last_status_score < oldest_home_score
|
||||||
|
|
||||||
|
# No need to get older statuses
|
||||||
|
query = query.where(id: oldest_home_score...)
|
||||||
end
|
end
|
||||||
|
|
||||||
statuses = target_account.statuses.list_eligible_visibility.includes(:preloadable_poll, :media_attachments, :account, reblog: :account).limit(limit)
|
statuses = query.to_a
|
||||||
crutches = build_crutches(list.account_id, statuses)
|
next if statuses.empty?
|
||||||
|
|
||||||
|
crutches = build_crutches(list.account_id, statuses, list: list)
|
||||||
|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
next if filter_from_home(status, list.account_id, crutches) || filter_from_list?(status, list)
|
next if filter_from_home(status, list.account_id, crutches, :list)
|
||||||
|
|
||||||
add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate)
|
add_to_feed(:list, list.id, status, aggregate_reblogs: aggregate)
|
||||||
end
|
end
|
||||||
|
@ -592,8 +610,9 @@ class FeedManager
|
||||||
# are going to be checked by the filtering methods
|
# are going to be checked by the filtering methods
|
||||||
# @param [Integer] receiver_id
|
# @param [Integer] receiver_id
|
||||||
# @param [Array<Status>] statuses
|
# @param [Array<Status>] statuses
|
||||||
|
# @param [List] list
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def build_crutches(receiver_id, statuses)
|
def build_crutches(receiver_id, statuses, list: nil)
|
||||||
crutches = {}
|
crutches = {}
|
||||||
|
|
||||||
crutches[:active_mentions] = crutches_active_mentions(statuses)
|
crutches[:active_mentions] = crutches_active_mentions(statuses)
|
||||||
|
@ -610,20 +629,33 @@ class FeedManager
|
||||||
arr
|
arr
|
||||||
end
|
end
|
||||||
|
|
||||||
lists = List.where(account_id: receiver_id, exclusive: true)
|
crutches[:following] = crutches_following(receiver_id, statuses, list)
|
||||||
|
|
||||||
crutches[:following] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true)
|
|
||||||
crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h
|
crutches[:languages] = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:account_id)).pluck(:target_account_id, :languages).to_h
|
||||||
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
crutches[:hiding_reblogs] = Follow.where(account_id: receiver_id, target_account_id: statuses.filter_map { |s| s.account_id if s.reblog? }, show_reblogs: false).pluck(:target_account_id).index_with(true)
|
||||||
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
crutches[:blocking] = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||||
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
crutches[:muting] = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).index_with(true)
|
||||||
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true)
|
crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.flat_map { |s| [s.account.domain, s.reblog&.account&.domain] }.compact).pluck(:domain).index_with(true)
|
||||||
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true)
|
crutches[:blocked_by] = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| [s.account_id, s.reblog&.account_id] }.flatten.compact).pluck(:account_id).index_with(true)
|
||||||
crutches[:exclusive_list_users] = ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true)
|
crutches[:exclusive_list_users] = crutches_exclusive_list_users(receiver_id, statuses) if list.blank?
|
||||||
|
|
||||||
crutches
|
crutches
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def crutches_exclusive_list_users(recipient_id, statuses)
|
||||||
|
lists = List.where(account_id: recipient_id, exclusive: true)
|
||||||
|
ListAccount.where(list: lists, account_id: statuses.map(&:account_id)).pluck(:account_id).index_with(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def crutches_following(recipient_id, statuses, list)
|
||||||
|
if list.blank? || list.show_followed?
|
||||||
|
Follow.where(account_id: recipient_id, target_account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:target_account_id).index_with(true)
|
||||||
|
elsif list.show_list?
|
||||||
|
ListAccount.where(list_id: list.id, account_id: statuses.filter_map(&:in_reply_to_account_id)).pluck(:account_id).index_with(true)
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def crutches_active_mentions(statuses)
|
def crutches_active_mentions(statuses)
|
||||||
Mention
|
Mention
|
||||||
.active
|
.active
|
||||||
|
|
Loading…
Reference in New Issue
Block a user