0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-05 19:53:47 +08:00
discourse/app/models/user_visit_daily_rollup.rb
Alan Guo Xiang Tan 395077a745
PERF: Use daily rollups for DAU/MAU reports (#41958)
DAU/MAU reports previously calculated every requested date from the full
user visit history, so dashboard requests became slower as that history
grew.

Persist the daily DAU and trailing-30-day MAU values and have the shared
report read only those rollups. The scheduled job builds all existing
history on its first successful run, then refreshes yesterday and today
every three hours. Replacements are atomic, so a failed calculation
leaves the previous report data intact.
2026-07-24 09:52:40 +08:00

43 lines
1.2 KiB
Ruby
Vendored

# frozen_string_literal: true
class UserVisitDailyRollup < ActiveRecord::Base
def self.fetch(start_date:, end_date:)
where(date: start_date.to_date..end_date.to_date)
.order(:date)
.pluck(:date, :dau, :mau)
.map { |date, dau, mau| { "date" => date, "dau" => dau, "mau" => mau } }
end
def self.aggregate(start_date:, end_date:)
rows = UserVisit.count_by_active_users(start_date, end_date)
transaction { replace!(start_date: start_date, end_date: end_date, rows: rows) }
nil
end
def self.replace!(start_date:, end_date:, rows:)
start_date = start_date.to_date
end_date = end_date.to_date
where(date: start_date..end_date).delete_all
return if rows.empty?
insert_all!(
rows.map { |row| { date: row.fetch("date"), dau: row.fetch("dau"), mau: row.fetch("mau") } },
)
end
private_class_method :replace!
end
# == Schema Information
#
# Table name: user_visit_daily_rollups
#
# id :bigint not null, primary key
# date :date not null
# dau :bigint not null
# mau :bigint not null
#
# Indexes
#
# index_user_visit_daily_rollups_on_date (date) UNIQUE
#