Skip to content

Commit 5396371

Browse files
committed
group results, count and minWpm into one redis call
1 parent 2e130a6 commit 5396371

File tree

8 files changed

+128
-142
lines changed

8 files changed

+128
-142
lines changed

backend/__tests__/__integration__/utils/daily-leaderboards.spec.ts

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,14 @@ describe("Daily Leaderboards", () => {
119119
true
120120
);
121121
//THEN
122-
expect(results).toEqual([
123-
{ rank: 1, ...bestResult },
124-
{ rank: 2, ...user2 },
125-
]);
122+
expect(results).toEqual({
123+
count: 2,
124+
minWpm: 20,
125+
entries: [
126+
{ rank: 1, ...bestResult },
127+
{ rank: 2, ...user2 },
128+
],
129+
});
126130
});
127131

128132
it("limits max amount of results", async () => {
@@ -135,7 +139,9 @@ describe("Daily Leaderboards", () => {
135139
.fill(0)
136140
.map(() => givenResult({ wpm: 20 + Math.random() * 100 }))
137141
);
138-
expect(await lb.getCount()).toEqual(maxResults);
142+
expect(
143+
await lb.getResults(0, 5, dailyLeaderboardsConfig, true)
144+
).toEqual(expect.objectContaining({ count: maxResults }));
139145
expect(await lb.getRank(bob.uid, dailyLeaderboardsConfig)).toEqual({
140146
rank: maxResults,
141147
...bob,
@@ -146,7 +152,9 @@ describe("Daily Leaderboards", () => {
146152

147153
//THEN
148154
//max count is still the same, but bob is no longer on the leaderboard
149-
expect(await lb.getCount()).toEqual(maxResults);
155+
expect(
156+
await lb.getResults(0, 5, dailyLeaderboardsConfig, true)
157+
).toEqual(expect.objectContaining({ count: maxResults }));
150158
expect(await lb.getRank(bob.uid, dailyLeaderboardsConfig)).toBeNull();
151159
});
152160
});
@@ -165,11 +173,15 @@ describe("Daily Leaderboards", () => {
165173
true
166174
);
167175
//THEN
168-
expect(results).toEqual([
169-
{ rank: 1, ...user2 },
170-
{ rank: 2, ...user1 },
171-
{ rank: 3, ...user3 },
172-
]);
176+
expect(results).toEqual({
177+
count: 3,
178+
minWpm: 40,
179+
entries: [
180+
{ rank: 1, ...user2 },
181+
{ rank: 2, ...user1 },
182+
{ rank: 3, ...user3 },
183+
],
184+
});
173185
});
174186
it("gets result for page", async () => {
175187
//GIVEN
@@ -187,10 +199,14 @@ describe("Daily Leaderboards", () => {
187199
true
188200
);
189201
//THEN
190-
expect(results).toEqual([
191-
{ rank: 3, ...user4 },
192-
{ rank: 4, ...user3 },
193-
]);
202+
expect(results).toEqual({
203+
count: 5,
204+
minWpm: 20,
205+
entries: [
206+
{ rank: 3, ...user4 },
207+
{ rank: 4, ...user3 },
208+
],
209+
});
194210
});
195211

196212
it("gets result without premium", async () => {
@@ -207,11 +223,15 @@ describe("Daily Leaderboards", () => {
207223
false
208224
);
209225
//THEN
210-
expect(results).toEqual([
211-
{ rank: 1, ...user2, isPremium: undefined },
212-
{ rank: 2, ...user1, isPremium: undefined },
213-
{ rank: 3, ...user3, isPremium: undefined },
214-
]);
226+
expect(results).toEqual({
227+
count: 3,
228+
minWpm: 40,
229+
entries: [
230+
{ rank: 1, ...user2, isPremium: undefined },
231+
{ rank: 2, ...user1, isPremium: undefined },
232+
{ rank: 3, ...user3, isPremium: undefined },
233+
],
234+
});
215235
});
216236

217237
it("should get for friends only", async () => {
@@ -220,6 +240,7 @@ describe("Daily Leaderboards", () => {
220240
const user2 = await givenResult({ wpm: 80 });
221241
const _user3 = await givenResult({ wpm: 70 });
222242
const user4 = await givenResult({ wpm: 60 });
243+
const _user5 = await givenResult({ wpm: 50 });
223244

224245
//WHEN
225246
const results = await lb.getResults(
@@ -230,10 +251,14 @@ describe("Daily Leaderboards", () => {
230251
[user2.uid, user4.uid]
231252
);
232253
//THEN
233-
expect(results).toEqual([
234-
{ rank: 2, friendsRank: 1, ...user2 },
235-
{ rank: 4, friendsRank: 2, ...user4 },
236-
]);
254+
expect(results).toEqual({
255+
count: 2,
256+
minWpm: 60,
257+
entries: [
258+
{ rank: 2, friendsRank: 1, ...user2 },
259+
{ rank: 4, friendsRank: 2, ...user4 },
260+
],
261+
});
237262
});
238263

239264
it("should get for friends only with page", async () => {
@@ -242,6 +267,7 @@ describe("Daily Leaderboards", () => {
242267
const user2 = await givenResult({ wpm: 100 });
243268
const _user3 = await givenResult({ wpm: 95 });
244269
const user4 = await givenResult({ wpm: 90 });
270+
const _user5 = await givenResult({ wpm: 70 });
245271

246272
//WHEN
247273

@@ -254,7 +280,11 @@ describe("Daily Leaderboards", () => {
254280
);
255281

256282
//THEN
257-
expect(results).toEqual([{ rank: 4, friendsRank: 3, ...user4 }]);
283+
expect(results).toEqual({
284+
count: 3,
285+
minWpm: 90,
286+
entries: [{ rank: 4, friendsRank: 3, ...user4 }],
287+
});
258288
});
259289

260290
it("should return empty list if no friends", async () => {
@@ -269,20 +299,11 @@ describe("Daily Leaderboards", () => {
269299
[]
270300
);
271301
//THEN
272-
expect(results).toEqual([]);
273-
});
274-
});
275-
276-
describe("minWpm", () => {
277-
it("gets min wpm", async () => {
278-
//GIVEN
279-
await givenResult({ wpm: 50 });
280-
await givenResult({ wpm: 60 });
281-
282-
//WHEN
283-
const minWpm = await lb.getMinWpm(dailyLeaderboardsConfig);
284-
//THEN
285-
expect(minWpm).toEqual(50);
302+
expect(results).toEqual({
303+
count: 0,
304+
minWpm: 0,
305+
entries: [],
306+
});
286307
});
287308
});
288309

@@ -299,23 +320,11 @@ describe("Daily Leaderboards", () => {
299320
});
300321
});
301322

302-
describe("getCount", () => {
303-
it("gets count", async () => {
304-
//GIVEN
305-
await givenResult({ wpm: 50 });
306-
await givenResult({ wpm: 60 });
307-
308-
//WHEN
309-
const count = await lb.getCount();
310-
//THEN
311-
expect(count).toEqual(2);
312-
});
313-
});
314-
315323
it("purgeUserFromDailyLeaderboards", async () => {
316324
//GIVEN
317325
const cheater = await givenResult({ wpm: 50 });
318-
const validUser = await givenResult();
326+
const user1 = await givenResult({ wpm: 60 });
327+
const user2 = await givenResult({ wpm: 40 });
319328

320329
//WHEN
321330
await DailyLeaderboards.purgeUserFromDailyLeaderboards(
@@ -325,7 +334,14 @@ describe("Daily Leaderboards", () => {
325334
//THEN
326335
expect(await lb.getRank(cheater.uid, dailyLeaderboardsConfig)).toBeNull();
327336
expect(await lb.getResults(0, 50, dailyLeaderboardsConfig, true)).toEqual(
328-
[{ rank: 1, ...validUser }]
337+
{
338+
count: 2,
339+
minWpm: 40,
340+
entries: [
341+
{ rank: 1, ...user1 },
342+
{ rank: 2, ...user2 },
343+
],
344+
}
329345
);
330346
});
331347

backend/__tests__/api/controllers/leaderboard.spec.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -457,31 +457,21 @@ describe("Loaderboard Controller", () => {
457457
const getFriendsUidsMock = vi.spyOn(FriendDal, "getFriendsUids");
458458

459459
const getResultMock = vi.fn();
460-
const getCountMock = vi.fn();
461-
const getMinWpmMock = vi.fn();
462460

463461
beforeEach(async () => {
464-
[
465-
getDailyLeaderboardMock,
466-
getFriendsUidsMock,
467-
getResultMock,
468-
getCountMock,
469-
getMinWpmMock,
470-
].forEach((it) => it.mockClear());
462+
[getDailyLeaderboardMock, getFriendsUidsMock, getResultMock].forEach(
463+
(it) => it.mockClear()
464+
);
471465

472466
vi.useFakeTimers();
473467
vi.setSystemTime(1722606812000);
474468
await dailyLeaderboardEnabled(true);
475469

476470
getDailyLeaderboardMock.mockReturnValue({
477471
getResults: getResultMock,
478-
getCount: getCountMock,
479-
getMinWpm: getMinWpmMock,
480472
} as any);
481473

482-
getResultMock.mockResolvedValue([]);
483-
getCountMock.mockResolvedValue(0);
484-
getMinWpmMock.mockResolvedValue(0);
474+
getResultMock.mockResolvedValue(null);
485475
});
486476

487477
afterEach(() => {
@@ -521,9 +511,11 @@ describe("Loaderboard Controller", () => {
521511
],
522512
};
523513

524-
getResultMock.mockResolvedValue(resultData);
525-
getCountMock.mockResolvedValue(2);
526-
getMinWpmMock.mockResolvedValue(10);
514+
getResultMock.mockResolvedValue({
515+
count: 2,
516+
minWpm: 10,
517+
entries: resultData,
518+
});
527519

528520
//WHEN
529521
const { body } = await mockApp
@@ -600,9 +592,7 @@ describe("Loaderboard Controller", () => {
600592
const page = 2;
601593
const pageSize = 25;
602594

603-
getResultMock.mockResolvedValue([]);
604-
getCountMock.mockResolvedValue(0);
605-
getMinWpmMock.mockResolvedValue(0);
595+
getResultMock.mockResolvedValue({ entries: [] });
606596

607597
//WHEN
608598
const { body } = await mockApp

backend/redis-scripts/get-results.lua

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,29 @@ local user_ids_csv = ARGV[4]
1818
local results = {}
1919
local scores = {}
2020
local ranks = {}
21+
local count = nil
22+
local min_score = {user_id = nil, score = nil}
2123

2224

23-
-- Filtered leaderboard
25+
-- filtered leaderboard
2426
if user_ids_csv ~= "" then
2527

2628
local filtered_user_ids = split_csv(user_ids_csv)
2729
local scored_users = {}
2830
for _, user_id in ipairs(filtered_user_ids) do
2931
local score = redis_call('ZSCORE', leaderboard_scores_key, user_id)
3032
if score then
31-
table.insert(scored_users, {user_id = user_id, score = tonumber(score)})
33+
local number_score = tonumber(score)
34+
table.insert(scored_users, {user_id = user_id, score = number_score})
3235
end
3336
end
3437
table.sort(scored_users, function(a, b) return a.score > b.score end)
3538

3639

40+
if #scored_users > 0 then
41+
min_score = {scored_users[#scored_users].user_id, scored_users[#scored_users].score}
42+
end
43+
count = #scored_users
3744

3845
for i = min_rank + 1, math.min(max_rank + 1, #scored_users) do
3946
local entry = scored_users[i]
@@ -54,10 +61,12 @@ if user_ids_csv ~= "" then
5461
end
5562

5663
end
57-
-- Global leaderboard
58-
else
5964

65+
else
66+
-- global leaderboard
6067
local scores_in_range = redis_call('ZRANGE', leaderboard_scores_key, min_rank, max_rank, 'REV')
68+
min_score = redis_call('ZRANGE', leaderboard_scores_key, 0, 0, 'WITHSCORES')
69+
count = redis_call('ZCARD', leaderboard_scores_key)
6170

6271
for _, user_id in ipairs(scores_in_range) do
6372
local result_data = redis_call('HGET', leaderboard_results_key, user_id)
@@ -72,4 +81,4 @@ else
7281
end
7382
end
7483

75-
return {results, scores, ranks}
84+
return {results, scores, count, min_score, ranks}

backend/src/api/controllers/leaderboard.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,10 @@ export async function getDailyLeaderboard(
160160
friendUids
161161
);
162162

163-
const minWpm = await dailyLeaderboard.getMinWpm(
164-
req.ctx.configuration.dailyLeaderboards
165-
);
166-
167-
const count = await dailyLeaderboard.getCount();
168-
169163
return new MonkeyResponse("Daily leaderboard retrieved", {
170-
entries: results,
171-
minWpm,
172-
count,
164+
entries: results?.entries ?? [],
165+
count: results?.count ?? 0,
166+
minWpm: results?.minWpm ?? 0,
173167
pageSize,
174168
});
175169
}

backend/src/init/redis.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export type RedisConnectionWithCustomMethods = Redis & {
3535
maxRank: number,
3636
withScores: string,
3737
userIds: string
38-
) => Promise<[string[], string[], string[]]>; //entries, scores(optional), ranks(optional)
38+
) => Promise<[string[], string[], string, string, string[]]>; //entries, scores(optional), count, min_score(optiona), ranks(optional)
3939
purgeResults: (
4040
keyCount: number,
4141
uid: string,

backend/src/services/weekly-xp-leaderboard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ export class WeeklyXpLeaderboard {
144144
weeklyXpLeaderboardResultsKey,
145145
minRank,
146146
maxRank,
147-
"true"
147+
"true",
148+
"" //TODO friends
148149
)) as string[][];
149150

150151
if (results === undefined) {

0 commit comments

Comments
 (0)