Skip to content

Commit

Permalink
Use caching for search results in order to increase performance on la…
Browse files Browse the repository at this point in the history
…rge queries.

Yes, this DOES hog memory. But I have a lot of it.
  • Loading branch information
Simyon264 committed Apr 13, 2024
1 parent 3f19fd7 commit e6c484c
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 13 deletions.
7 changes: 5 additions & 2 deletions Server/Api/ReplayController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.EntityFrameworkCore;
using Shared;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;
using Server.Helpers;

namespace Server.Api;
Expand All @@ -12,10 +13,12 @@ namespace Server.Api;
public class ReplayController : ControllerBase
{
private readonly ReplayDbContext _context;
private readonly IMemoryCache _cache;

public ReplayController(ReplayDbContext context)
public ReplayController(ReplayDbContext context, IMemoryCache cache)
{
_context = context;
_cache = cache;
}

[HttpPost]
Expand Down Expand Up @@ -96,7 +99,7 @@ public async Task<ActionResult> SearchReplays(
return BadRequest("The page number cannot be negative.");
}

var found = ReplayParser.SearchReplays(searchMode, query, _context, page, Constants.ReplaysPerPage);
var found = ReplayParser.SearchReplays(searchMode, query, _context, page, Constants.ReplaysPerPage, _cache);

var pageCount = Paginator.GetPageCount(found.Item2, Constants.ReplaysPerPage);

Expand Down
50 changes: 39 additions & 11 deletions Server/ReplayParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using HtmlAgilityPack;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Caching.Memory;
using Serilog;
using Server.Api;
using Shared;
Expand Down Expand Up @@ -295,11 +296,21 @@ public static HttpClient CreateHttpClient()
/// <exception cref="NotImplementedException">
/// Thrown when the search mode is not implemented.
/// </exception>
public static (List<Replay>, int) SearchReplays(SearchMode mode, string query, ReplayDbContext context, int page, int pageSize)
public static (List<Replay>, int) SearchReplays(SearchMode mode, string query, ReplayDbContext context, int page, int pageSize, IMemoryCache cache)
{
var cacheKey = $"{mode}-{query}-{pageSize}";
if (cache.TryGetValue(cacheKey, out List<(List<Replay>, int)> cachedResult))
{
if (page < cachedResult.Count)
{
return cachedResult[page];
}
}

var stopWatch = new Stopwatch();
stopWatch.Start();
var queryable = context.Replays.AsQueryable();


IIncludableQueryable<Player, Replay?>? players;
IQueryable<int?>? replayIds;
Expand Down Expand Up @@ -350,18 +361,35 @@ public static (List<Replay>, int) SearchReplays(SearchMode mode, string query, R
}

var totalItems = queryable.Count();

// Apply pagination on the database query
var list = (queryable
.Include(r => r.RoundEndPlayers)
.OrderByDescending(r => r.Date ?? DateTime.MinValue).Take(Constants.SearchLimit).ToList()
.Skip(page * pageSize)

// Get all results and store them in the cache
var allResults = queryable
.Include(r => r.RoundEndPlayers)
.OrderByDescending(r => r.Date ?? DateTime.MinValue)
.Take(Constants.SearchLimit)
.ToList();

var paginatedResults = new List<(List<Replay>, int)>();
for (int i = 0; i * pageSize < allResults.Count; i++)
{
var paginatedList = allResults
.Skip(i * pageSize)
.Take(pageSize)
.ToList(),
totalItems);

.ToList();

paginatedResults.Add((paginatedList, totalItems));
}

cache.Set(cacheKey, paginatedResults, TimeSpan.FromMinutes(5));

stopWatch.Stop();
Log.Information("Search took " + stopWatch.ElapsedMilliseconds + "ms.");
return list;

if (page < paginatedResults.Count)
{
return paginatedResults[page];
}

return (new List<Replay>(), 0);
}
}

0 comments on commit e6c484c

Please sign in to comment.