-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from Atralupus/feat/ranking
Implement ranking system with redis sortedset
- Loading branch information
Showing
9 changed files
with
169 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
using StackExchange.Redis; | ||
|
||
namespace ArenaService.Repositories; | ||
|
||
public interface IRankingRepository | ||
{ | ||
Task UpdateScoreAsync(string leaderboardKey, int participantId, int scoreChange); | ||
|
||
Task<int?> GetRankAsync(string leaderboardKey, int participantId); | ||
|
||
Task<int?> GetScoreAsync(string leaderboardKey, int participantId); | ||
|
||
Task<List<(int Rank, int ParticipantId, int Score)>> GetTopRankingsAsync( | ||
string leaderboardKey, | ||
int topN | ||
); | ||
|
||
Task<List<(int Rank, int ParticipantId, int Score)>> GetRankingsWithPaginationAsync( | ||
string leaderboardKey, | ||
int pageNumber, | ||
int pageSize | ||
); | ||
|
||
Task SyncLeaderboardAsync(string leaderboardKey, List<(int ParticipantId, int Score)> entries); | ||
} | ||
|
||
public class RankingRepository : IRankingRepository | ||
{ | ||
private readonly IDatabase _redis; | ||
|
||
public RankingRepository(IConnectionMultiplexer redis) | ||
{ | ||
_redis = redis.GetDatabase(); | ||
} | ||
|
||
public async Task UpdateScoreAsync(string leaderboardKey, int participantId, int scoreChange) | ||
{ | ||
await _redis.SortedSetIncrementAsync( | ||
leaderboardKey, | ||
$"participant:{participantId}", | ||
scoreChange | ||
); | ||
} | ||
|
||
public async Task<int?> GetRankAsync(string leaderboardKey, int participantId) | ||
{ | ||
var rank = await _redis.SortedSetRankAsync( | ||
leaderboardKey, | ||
$"participant:{participantId}", | ||
Order.Descending | ||
); | ||
return rank.HasValue ? (int)rank.Value + 1 : null; | ||
} | ||
|
||
public async Task<int?> GetScoreAsync(string leaderboardKey, int participantId) | ||
{ | ||
var score = await _redis.SortedSetScoreAsync( | ||
leaderboardKey, | ||
$"participant:{participantId}" | ||
); | ||
return score.HasValue ? (int)score.Value : null; | ||
} | ||
|
||
public async Task<List<(int Rank, int ParticipantId, int Score)>> GetTopRankingsAsync( | ||
string leaderboardKey, | ||
int topN | ||
) | ||
{ | ||
var topRankings = await _redis.SortedSetRangeByRankWithScoresAsync( | ||
leaderboardKey, | ||
0, | ||
topN - 1, | ||
Order.Descending | ||
); | ||
|
||
return topRankings | ||
.Select( | ||
(entry, index) => | ||
{ | ||
var participantId = int.Parse(entry.Element.ToString().Split(':')[1]); | ||
return (Rank: index + 1, ParticipantId: participantId, Score: (int)entry.Score); | ||
} | ||
) | ||
.ToList(); | ||
} | ||
|
||
public async Task< | ||
List<(int Rank, int ParticipantId, int Score)> | ||
> GetRankingsWithPaginationAsync(string leaderboardKey, int pageNumber, int pageSize) | ||
{ | ||
int start = (pageNumber - 1) * pageSize; | ||
int end = start + pageSize - 1; | ||
|
||
var rankedParticipants = await _redis.SortedSetRangeByRankWithScoresAsync( | ||
leaderboardKey, | ||
start, | ||
end, | ||
Order.Descending | ||
); | ||
|
||
return rankedParticipants | ||
.Select( | ||
(entry, index) => | ||
{ | ||
var participantId = int.Parse(entry.Element.ToString().Split(':')[1]); | ||
return ( | ||
Rank: start + index + 1, | ||
ParticipantId: participantId, | ||
Score: (int)entry.Score | ||
); | ||
} | ||
) | ||
.ToList(); | ||
} | ||
|
||
public async Task SyncLeaderboardAsync( | ||
string leaderboardKey, | ||
List<(int ParticipantId, int Score)> entries | ||
) | ||
{ | ||
foreach (var entry in entries) | ||
{ | ||
await _redis.SortedSetAddAsync( | ||
leaderboardKey, | ||
$"participant:{entry.ParticipantId}", | ||
entry.Score | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters