Skip to content

Commit c786aa5

Browse files
Add support for caching the whole scoreboard.
Still WIP.
1 parent 2d6f4ff commit c786aa5

16 files changed

+716
-493
lines changed

etc/db-config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@
323323
default_value: true
324324
public: true
325325
description: If disabled, no ranking information is shown to contestants.
326+
- name: cache_full_scoreboard
327+
type: bool
328+
default_value: false
329+
public: false
330+
description: |
331+
If enabled, will cache the whole scoreboard.
332+
Make sure to clear the scoreboard cache after making changes to data
333+
like teams, categories, affiliations or the contest when enabled.
326334
- category: Authentication
327335
description: Options related to authentication.
328336
items:

webapp/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"symfony/web-profiler-bundle": "6.4.*",
106106
"symfony/yaml": "6.4.*",
107107
"twbs/bootstrap": "^5.2.0",
108+
"twig/cache-extra": "^3.11",
108109
"twig/extra-bundle": "^3.5",
109110
"twig/markdown-extra": "^3.5",
110111
"twig/string-extra": "^3.5",

webapp/composer.lock

Lines changed: 66 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webapp/src/Controller/Jury/ConfigController.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ public function indexAction(EventLogService $eventLogService, Request $request):
9595
if ($category === 'Judging') {
9696
$needsRejudging = true;
9797
}
98+
if ($key === 'cache_full_scoreboard') {
99+
$needsRefresh = true;
100+
}
98101
}
99102

100103
if ($needsRefresh) {

webapp/src/Controller/Jury/JuryMiscController.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use App\Service\ConfigurationService;
1515
use App\Service\DOMJudgeService;
1616
use App\Service\EventLogService;
17+
use App\Service\ScoreboardCacheService;
1718
use App\Service\ScoreboardService;
1819
use Doctrine\ORM\EntityManagerInterface;
1920
use Doctrine\ORM\Query\Expr\Join;
@@ -188,13 +189,16 @@ public function ajaxDataAction(Request $request, string $datatype): JsonResponse
188189

189190
#[IsGranted('ROLE_ADMIN')]
190191
#[Route(path: '/refresh-cache', name: 'jury_refresh_cache')]
191-
public function refreshCacheAction(Request $request, ScoreboardService $scoreboardService): Response
192-
{
192+
public function refreshCacheAction(
193+
Request $request,
194+
ScoreboardService $scoreboardService,
195+
ScoreboardCacheService $cache,
196+
): Response {
193197
// Note: we use a XMLHttpRequest here as Symfony does not support
194198
// streaming Twig output.
195199

196200
$contests = $this->dj->getCurrentContests();
197-
if ($cid = $request->request->get('cid')) {
201+
if ($cid = $request->get('cid')) {
198202
if (!isset($contests[$cid])) {
199203
throw new BadRequestHttpException(sprintf('Contest %s not found', $cid));
200204
}
@@ -204,17 +208,29 @@ public function refreshCacheAction(Request $request, ScoreboardService $scoreboa
204208
$contests = [$contest->getCid() => $contest];
205209
}
206210

211+
$onlyFullCache = $request->get('only_full_cache');
212+
207213
if ($request->isXmlHttpRequest() && $request->isMethod('POST')) {
208214
$progressReporter = function (int $progress, string $log, ?string $message = null) {
209215
echo $this->dj->jsonEncode(['progress' => $progress, 'log' => htmlspecialchars($log), 'message' => $message]);
210216
ob_flush();
211217
flush();
212218
};
213-
return $this->streamResponse($this->requestStack, function () use ($contests, $progressReporter, $scoreboardService) {
219+
return $this->streamResponse($this->requestStack, function () use (
220+
$contests,
221+
$progressReporter,
222+
$scoreboardService,
223+
$onlyFullCache,
224+
$cache
225+
) {
214226
$timeStart = microtime(true);
215227

216228
foreach ($contests as $contest) {
217-
$scoreboardService->refreshCache($contest, $progressReporter);
229+
if ($onlyFullCache) {
230+
$cache->invalidate($contest);
231+
} else {
232+
$scoreboardService->refreshCache($contest, $progressReporter);
233+
}
218234
}
219235

220236
$timeEnd = microtime(true);

webapp/src/Service/RejudgingService.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ public function __construct(
3232
protected readonly DOMJudgeService $dj,
3333
protected readonly ScoreboardService $scoreboardService,
3434
protected readonly EventLogService $eventLogService,
35-
protected readonly BalloonService $balloonService
35+
protected readonly BalloonService $balloonService,
36+
protected readonly ScoreboardCacheService $cache,
3637
) {}
3738

3839
/**
@@ -381,6 +382,7 @@ public function finishRejudging(Rejudging $rejudging, string $action, ?callable
381382
}
382383
$this->scoreboardService->updateRankCache($contest, $team);
383384
}
385+
$this->cache->invalidate($contest);
384386
}
385387
}
386388

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace App\Service;
4+
5+
use App\Entity\Contest;
6+
use Symfony\Contracts\Cache\ItemInterface;
7+
use Symfony\Contracts\Cache\TagAwareCacheInterface;
8+
9+
class ScoreboardCacheService
10+
{
11+
public function __construct(
12+
protected readonly ConfigurationService $config,
13+
protected readonly TagAwareCacheInterface $twigCache,
14+
) {}
15+
16+
/**
17+
* @template T
18+
* @param Contest $contest
19+
* @param string $cacheKey
20+
* @param callable(): T $callable
21+
*
22+
* @return T
23+
*/
24+
public function cacheScoreboardData(
25+
Contest $contest,
26+
string $cacheKey,
27+
callable $callable
28+
): mixed {
29+
if (!$this->config->get('cache_full_scoreboard')) {
30+
return $callable();
31+
}
32+
33+
return $this->twigCache->get($cacheKey, function (ItemInterface $item) use (
34+
$callable,
35+
$contest
36+
) {
37+
$item->tag('scoreboard_' . $contest->getCid());
38+
39+
return $callable();
40+
});
41+
}
42+
43+
public function invalidate(Contest $contest): void
44+
{
45+
$this->twigCache->invalidateTags(['scoreboard_' . $contest->getCid()]);
46+
}
47+
}

0 commit comments

Comments
 (0)