Skip to content

Commit 684b673

Browse files
committed
Improve loading time for the achievements leaderboard.
Currently the user achievements are obtained from the database in the user loop. This takes a long time when there are lots of users. So instead, this gets all user achievements for all users before the loop and references them by user id and then achievement id. Testing this on a course for 5000 users shows this gives a significant speed up on load time for the page. With the develop branch it takes around 25 seconds, and with this branch it takes around 3 seconds. Note that there were also two redundant queries (one that listed all achievements and then one that fetched all achievements on lines 49 and 50 in the develop branch) for which the data from those queries was not even used. Those were removed.
1 parent 2d4cbcc commit 684b673

File tree

1 file changed

+15
-12
lines changed

1 file changed

+15
-12
lines changed

lib/WeBWorK/ContentGenerator/AchievementsLeaderboard.pm

+15-12
Original file line numberDiff line numberDiff line change
@@ -46,37 +46,40 @@ sub initialize ($c) {
4646
my %globalUserAchievements =
4747
map { $_->user_id => $_ } $db->getGlobalUserAchievementsWhere({ user_id => { not_like => 'set_id:%' } });
4848

49-
my @allBadgeIDs = $db->listAchievements;
50-
my @allBadges = @allBadgeIDs ? sortAchievements($db->getAchievements(@allBadgeIDs)) : ();
51-
5249
$c->{showUserNames} = $c->authz->hasPermissions($c->{userName}, 'view_leaderboard_usernames');
5350
$c->{showLevels} = 0; # Hide level column unless at least one user has a level achievement.
5451

52+
my %allUserAchievements;
53+
for ($db->getUserAchievementsWhere({
54+
user_id => { not_like => 'set_id:%' },
55+
achievement_id => [ map { $_->achievement_id } grep { $_->category ne 'level' } @achievements ],
56+
}))
57+
{
58+
$allUserAchievements{ $_->user_id }{ $_->achievement_id } = $_;
59+
}
60+
5561
my @rows;
5662
for my $user ($db->getUsersWhere({ user_id => { not_like => 'set_id:%' } })) {
5763
# Only include users who can be shown in stats.
5864
next unless $ce->status_abbrev_has_behavior($user->status, 'include_in_stats');
5965

66+
my $globalData = $globalUserAchievements{ $user->user_id };
67+
my $userAchievements = $allUserAchievements{ $user->user_id };
68+
6069
# Skip unless user has achievement data.
61-
my $globalData = $globalUserAchievements{ $user->user_id };
62-
next unless $globalData;
70+
next unless $globalData && $userAchievements;
6371

6472
my $level = $globalData->level_achievement_id ? $achievementsById{ $globalData->level_achievement_id } : '';
6573

66-
my %userAchievements = map { $_->achievement_id => $_ } $db->getUserAchievementsWhere({
67-
user_id => $user->user_id,
68-
achievement_id => [ map { $_->achievement_id } grep { $_->category ne 'level' } @achievements ],
69-
});
70-
7174
my @badges;
7275
for my $achievement (@achievements) {
7376
# Skip level achievements and only show earned achievements.
7477
last if $achievement->category eq 'level';
7578

7679
push(@badges, $achievement)
77-
if $userAchievements{ $achievement->achievement_id }
80+
if $userAchievements->{ $achievement->achievement_id }
7881
&& $achievement->enabled
79-
&& $userAchievements{ $achievement->achievement_id }->earned;
82+
&& $userAchievements->{ $achievement->achievement_id }->earned;
8083
}
8184

8285
push(@rows, [ $globalData->achievement_points || 0, $level, $user, \@badges ]);

0 commit comments

Comments
 (0)