2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-02-25 01:19:12 -05:00
|
|
|
class RandomTopicSelector
|
|
|
|
|
|
|
|
BACKFILL_SIZE = 3000
|
|
|
|
BACKFILL_LOW_WATER_MARK = 500
|
|
|
|
|
|
|
|
def self.backfill(category = nil)
|
2017-09-07 21:54:13 -04:00
|
|
|
exclude = category&.topic_id
|
2015-02-25 01:19:12 -05:00
|
|
|
|
|
|
|
options = {
|
2017-03-01 12:03:12 -05:00
|
|
|
per_page: category ? category.num_featured_topics : 3,
|
2015-02-25 01:19:12 -05:00
|
|
|
visible: true,
|
|
|
|
no_definitions: true
|
|
|
|
}
|
|
|
|
|
|
|
|
options[:except_topic_ids] = [category.topic_id] if exclude
|
|
|
|
|
2018-01-15 00:13:29 -05:00
|
|
|
if category
|
|
|
|
options[:category] = category.id
|
|
|
|
# NOTE: at the moment this site setting scopes tightly to a category (excluding subcats)
|
|
|
|
# this is done so we don't populate a junk cache
|
|
|
|
if SiteSetting.limit_suggested_to_category
|
|
|
|
options[:no_subcategories] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
# don't leak private categories into the "everything" group
|
2020-06-24 01:51:30 -04:00
|
|
|
options[:guardian] = Guardian.new(Discourse.system_user)
|
2018-01-15 00:13:29 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
query = TopicQuery.new(nil, options)
|
2016-07-03 20:34:54 -04:00
|
|
|
|
2015-02-25 01:19:12 -05:00
|
|
|
results = query.latest_results.order('RANDOM()')
|
|
|
|
.where(closed: false, archived: false)
|
2016-07-03 20:34:54 -04:00
|
|
|
.where("topics.created_at > ?", SiteSetting.suggested_topics_max_days_old.days.ago)
|
2015-02-25 01:19:12 -05:00
|
|
|
.limit(BACKFILL_SIZE)
|
|
|
|
.reorder('RANDOM()')
|
|
|
|
.pluck(:id)
|
|
|
|
|
|
|
|
key = cache_key(category)
|
2017-09-07 21:54:13 -04:00
|
|
|
|
|
|
|
if results.present?
|
2022-05-09 18:19:02 -04:00
|
|
|
Discourse.redis.multi do |transaction|
|
|
|
|
transaction.rpush(key, results)
|
|
|
|
transaction.expire(key, 2.days)
|
2017-09-07 21:54:13 -04:00
|
|
|
end
|
2015-02-25 01:19:12 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
results
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.next(count, category = nil)
|
|
|
|
key = cache_key(category)
|
|
|
|
|
|
|
|
results = []
|
|
|
|
|
2015-10-02 01:00:51 -04:00
|
|
|
return results if count < 1
|
2015-02-25 01:19:12 -05:00
|
|
|
|
2022-05-09 18:19:02 -04:00
|
|
|
results = Discourse.redis.multi do |transaction|
|
|
|
|
transaction.lrange(key, 0, count - 1)
|
|
|
|
transaction.ltrim(key, count, -1)
|
2015-02-25 01:19:12 -05:00
|
|
|
end
|
|
|
|
|
2016-03-02 19:26:45 -05:00
|
|
|
if !results.is_a?(Array) # Redis is in readonly mode
|
2019-12-03 04:05:53 -05:00
|
|
|
results = Discourse.redis.lrange(key, 0, count - 1)
|
2016-03-02 19:26:45 -05:00
|
|
|
else
|
|
|
|
results = results[0]
|
|
|
|
end
|
|
|
|
|
2015-10-02 01:00:51 -04:00
|
|
|
results.map!(&:to_i)
|
|
|
|
|
|
|
|
left = count - results.length
|
|
|
|
|
2015-02-25 01:19:12 -05:00
|
|
|
backfilled = false
|
|
|
|
if left > 0
|
|
|
|
ids = backfill(category)
|
|
|
|
backfilled = true
|
|
|
|
results += ids[0...count]
|
|
|
|
results.uniq!
|
|
|
|
results = results[0...count]
|
|
|
|
end
|
|
|
|
|
2019-12-03 04:05:53 -05:00
|
|
|
if !backfilled && Discourse.redis.llen(key) < BACKFILL_LOW_WATER_MARK
|
2015-02-25 01:19:12 -05:00
|
|
|
Scheduler::Defer.later("backfill") do
|
|
|
|
backfill(category)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
results
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.cache_key(category = nil)
|
2017-09-07 21:54:13 -04:00
|
|
|
"random_topic_cache_#{category&.id}"
|
2015-02-25 01:19:12 -05:00
|
|
|
end
|
|
|
|
|
2018-05-22 18:39:15 -04:00
|
|
|
def self.clear_cache!
|
2019-12-03 04:05:53 -05:00
|
|
|
Discourse.redis.delete_prefixed(cache_key)
|
2018-05-22 18:39:15 -04:00
|
|
|
end
|
|
|
|
|
2015-02-25 01:19:12 -05:00
|
|
|
end
|