hackatime/app/jobs/leaderboard_update_job.rb
Mahad Kalam 8ce245d8c4
make leaderboards go vrooom (#1120)
* make leaderboards go vrooom

* goog
2026-03-30 13:58:18 +01:00

75 lines
2.3 KiB
Ruby

class LeaderboardUpdateJob < ApplicationJob
queue_as :latency_10s
include GoodJob::ActiveJobExtensions::Concurrency
# Limits concurrency to 1 job per period/date combination
good_job_control_concurrency_with(
key: -> { "leaderboard_#{arguments[0] || 'daily'}_#{arguments[1] || Date.current.to_s}" },
total: 1,
drop: true
)
def perform(period = :daily, date = Date.current, force_update: false)
date = LeaderboardDateRange.normalize_date(date, period)
build_leaderboard(date, period, force_update)
end
private
def build_leaderboard(date, period, force_update = false)
board = ::Leaderboard.find_or_create_by!(
start_date: date,
period_type: period,
timezone_utc_offset: nil
)
return board if board.finished_generating_at.present? && !force_update
Rails.logger.info "Building leaderboard for #{period} on #{date}"
range = LeaderboardDateRange.calculate(date, period)
timestamp = Time.current
eligible_users = User.where.not(github_uid: nil)
.where.not(trust_level: User.trust_levels[:red])
ActiveRecord::Base.transaction do
heartbeat_query = Heartbeat.where(user_id: eligible_users.select(:id), time: range)
.leaderboard_eligible
data = heartbeat_query.group(:user_id).duration_seconds
.filter { |_, seconds| seconds > 60 }
streaks = Heartbeat.daily_streaks_for_users(data.keys, exclude_browser_time: true)
entries = data.map do |user_id, seconds|
{
leaderboard_id: board.id,
user_id: user_id,
total_seconds: seconds,
streak_count: streaks[user_id] || 0,
created_at: timestamp,
updated_at: timestamp
}
end
LeaderboardEntry.upsert_all(entries, unique_by: %i[leaderboard_id user_id]) if entries.any?
if data.keys.any?
board.entries.where.not(user_id: data.keys).delete_all
else
board.entries.delete_all
end
board.update!(finished_generating_at: timestamp)
end
# Cache the board
cache_key = LeaderboardCache.global_key(period, date)
LeaderboardCache.write(cache_key, board)
Rails.logger.debug "Persisted leaderboard for #{period} with #{board.entries.count} entries"
board
end
end