selase
(Selase Krakani)
May 19, 2025, 2:44pm
21
I don’t have the full trace, but I believe the error happens during gamification_score
serialization for the user card.
Given the transient nature of these materialized views, a score lookup isn’t expected to cause a system-wide error, it should fall back to a “default score” when the view doesn’t exist.
There’s a bug in the materialized view existence check . It reports that a view exists when it actually doesn’t (at least not in the current public
schema), which leads to a lookup on a non-existent view resulting in the error.
I think what happened here was, the materialized view was present in the backup
schema created as part of the restore but not the public
schema. The backup
schema is retained for a while after the restore.
.includes(:flair_upload)
.all
.map { |group| BasicGroupSerializer.new(group, root: false, scope: self.scope).as_json },
gamification_leaderboards:
DiscourseGamification::GamificationLeaderboard.all.map do |leaderboard|
LeaderboardSerializer.new(leaderboard, root: false).as_json
end,
}
end
add_to_serializer(:user_card, :gamification_score) { object.gamification_score }
add_to_serializer(:site, :default_gamification_leaderboard_id) do
DiscourseGamification::GamificationLeaderboard.first&.id
end
SeedFu.fixture_paths << Rails
.root
.join("plugins", "discourse-gamification", "db", "fixtures")
.to_s
on(:site_setting_changed) do |name|
DEFAULT_SCORE = 0
def gamification_score
return DEFAULT_SCORE if !default_leaderboard
DiscourseGamification::GamificationLeaderboard.find_position_by(
leaderboard_id: default_leaderboard.id,
period: "all_time",
for_user_id: self.id,
)&.total_score || DEFAULT_SCORE
rescue DiscourseGamification::LeaderboardCachedView::NotReadyError
Jobs.enqueue(Jobs::GenerateLeaderboardPositions, leaderboard_id: default_leaderboard.id)
DEFAULT_SCORE
end
def default_leaderboard
@default_leaderboard ||= DiscourseGamification::GamificationLeaderboard.select(:id).first
end
end
end
def scores(period: "all_time", page: 0, for_user_id: false, limit: nil, offset: nil)
user_filter_condition = for_user_id ? ["users.id = ?", for_user_id] : [nil]
if mview_exists?(period)
User
.where(*user_filter_condition)
.joins("INNER JOIN #{mview_name(period)} p ON p.user_id = users.id")
.select(
"users.id, users.name, users.username, users.uploaded_avatar_id, p.total_score, p.position",
)
.limit(limit)
.offset(offset)
.order(position: :asc, id: :asc)
.load
else
raise NotReadyError.new(I18n.t("errors.leaderboard_positions_not_ready"))
end
end
I’ve got a PR ready with a fix.
main
← fix/materialized-view-presence-check
opened 02:16PM - 19 May 25 UTC
Currently, `mview_exists?` returns true if the materialized view existed anywher… e in the database, regardless of the schema. This caused issues after backup restores, where the views may exist in the `backup` schema created during the restore but not in the current public schema.
This change ensures the check is scoped to the current schema.
3 Likes