2021-04-12 10:37:14 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class Scheduler::FollowRecommendationsScheduler
|
|
|
|
include Sidekiq::Worker
|
|
|
|
include Redisable
|
|
|
|
|
|
|
|
sidekiq_options retry: 0
|
|
|
|
|
|
|
|
# The maximum number of accounts that can be requested in one page from the
|
|
|
|
# API is 80, and the suggestions API does not allow pagination. This number
|
|
|
|
# leaves some room for accounts being filtered during live access
|
|
|
|
SET_SIZE = 100
|
|
|
|
|
|
|
|
def perform
|
|
|
|
# Maintaining a materialized view speeds-up subsequent queries significantly
|
|
|
|
AccountSummary.refresh
|
2021-05-05 20:04:52 +00:00
|
|
|
FollowRecommendation.refresh
|
2021-04-12 10:37:14 +00:00
|
|
|
|
2022-02-08 00:53:49 +00:00
|
|
|
fallback_recommendations = FollowRecommendation.order(rank: :desc).limit(SET_SIZE)
|
2021-04-12 10:37:14 +00:00
|
|
|
|
2022-02-08 00:53:49 +00:00
|
|
|
I18n.available_locales.map { |locale| locale.to_s.split(/[_-]/).first }.uniq.each do |locale|
|
2021-04-12 10:37:14 +00:00
|
|
|
recommendations = begin
|
|
|
|
if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
|
2022-02-08 00:53:49 +00:00
|
|
|
FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.account_id, recommendation.rank] }
|
2021-04-12 10:37:14 +00:00
|
|
|
else
|
2022-02-08 00:53:49 +00:00
|
|
|
[]
|
2021-04-12 10:37:14 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Use language-agnostic results if there are not enough language-specific ones
|
2022-02-08 00:53:49 +00:00
|
|
|
missing = SET_SIZE - recommendations.size
|
|
|
|
|
|
|
|
if missing.positive? && fallback_recommendations.size.positive?
|
|
|
|
max_fallback_rank = fallback_recommendations.first.rank || 0
|
|
|
|
|
|
|
|
# Language-specific results should be above language-agnostic ones,
|
|
|
|
# otherwise language-agnostic ones will always overshadow them
|
|
|
|
recommendations.map! { |(account_id, rank)| [account_id, rank + max_fallback_rank] }
|
2021-04-12 10:37:14 +00:00
|
|
|
|
|
|
|
added = 0
|
|
|
|
|
2022-02-08 00:53:49 +00:00
|
|
|
fallback_recommendations.each do |recommendation|
|
|
|
|
next if recommendations.any? { |(account_id, _)| account_id == recommendation.account_id }
|
2021-04-12 10:37:14 +00:00
|
|
|
|
2022-02-08 00:53:49 +00:00
|
|
|
recommendations << [recommendation.account_id, recommendation.rank]
|
2021-04-12 10:37:14 +00:00
|
|
|
added += 1
|
|
|
|
|
|
|
|
break if added >= missing
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
redis.pipelined do
|
|
|
|
redis.del(key(locale))
|
|
|
|
|
2022-02-08 00:53:49 +00:00
|
|
|
recommendations.each do |(account_id, rank)|
|
|
|
|
redis.zadd(key(locale), rank, account_id)
|
2021-04-12 10:37:14 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def key(locale)
|
|
|
|
"follow_recommendations:#{locale}"
|
|
|
|
end
|
|
|
|
end
|