forked from discourse/discourse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
random_topic_selector.rb
94 lines (72 loc) · 2.25 KB
/
random_topic_selector.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# frozen_string_literal: true
class RandomTopicSelector
BACKFILL_SIZE = 3000
BACKFILL_LOW_WATER_MARK = 500
def self.backfill(category = nil)
exclude = category&.topic_id
options = {
per_page: category ? category.num_featured_topics : 3,
visible: true,
no_definitions: true,
}
options[:except_topic_ids] = [category.topic_id] if exclude
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
options[:no_subcategories] = true if SiteSetting.limit_suggested_to_category
# don't leak private categories into the "everything" group
options[:guardian] = Guardian.new(Discourse.system_user)
end
query = TopicQuery.new(nil, options)
results =
query
.latest_results
.order("RANDOM()")
.where(closed: false, archived: false)
.where("topics.created_at > ?", SiteSetting.suggested_topics_max_days_old.days.ago)
.limit(BACKFILL_SIZE)
.reorder("RANDOM()")
.pluck(:id)
key = cache_key(category)
if results.present?
Discourse.redis.multi do |transaction|
transaction.rpush(key, results)
transaction.expire(key, 2.days)
end
end
results
end
def self.next(count, category = nil)
key = cache_key(category)
results = []
return results if count < 1
results =
Discourse.redis.multi do |transaction|
transaction.lrange(key, 0, count - 1)
transaction.ltrim(key, count, -1)
end
if !results.is_a?(Array) # Redis is in readonly mode
results = Discourse.redis.lrange(key, 0, count - 1)
else
results = results[0]
end
results.map!(&:to_i)
left = count - results.length
backfilled = false
if left > 0
ids = backfill(category)
backfilled = true
results += ids[0...count]
results.uniq!
results = results[0...count]
end
if !backfilled && Discourse.redis.llen(key) < BACKFILL_LOW_WATER_MARK
Scheduler::Defer.later("backfill") { backfill(category) }
end
results
end
def self.cache_key(category = nil)
"random_topic_cache_#{category&.id}"
end
end