Class: RandomTopicSelector

Inherits:
Object
  • Object
show all
Defined in:
app/services/random_topic_selector.rb

Constant Summary collapse

BACKFILL_SIZE =
3000
BACKFILL_LOW_WATER_MARK =
500

Class Method Summary collapse

Class Method Details

.backfill(category = nil) ⇒ Object



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
# File 'app/services/random_topic_selector.rb', line 7

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

.cache_key(category = nil) ⇒ Object



91
92
93
# File 'app/services/random_topic_selector.rb', line 91

def self.cache_key(category = nil)
  "random_topic_cache_#{category&.id}"
end

.next(count, category = nil) ⇒ Object



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
# File 'app/services/random_topic_selector.rb', line 52

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