Skip to content

Commit

Permalink
Check that last seen event ID exists while emitting the event feed
Browse files Browse the repository at this point in the history
This will throw an error, for example, when the database gets
reloaded while there is a running process serving the event feed
to a connected client. We encountered this problem at WF Astana.
  • Loading branch information
DOMjudge team authored and eldering committed Sep 18, 2024
1 parent 0d62cb0 commit 0f9a160
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions webapp/src/Controller/API/ContestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
Expand Down Expand Up @@ -654,6 +655,7 @@ public function getEventFeedAction(
$response->setCallback(function () use ($format, $cid, $contest, $request, $since_id, $types, $strict, $stream, $metadataFactory, $kernel) {
$lastUpdate = 0;
$lastIdSent = max(0, $since_id); // Don't try to look for event_id=0
$lastIdExists = $since_id === -1;
$typeFilter = false;
if ($types) {
$typeFilter = explode(',', $types);
Expand Down Expand Up @@ -721,19 +723,31 @@ public function getEventFeedAction(
// Add missing state events that should have happened already.
$this->eventLogService->addMissingStateEvents($contest);

// We fetch *all* events after the last seen to check that
// We fetch *all* events from the last seen to check that
// we don't skip events that are committed out of order.
// This includes the last seen event itself, just to check
// that the database is consistent and, for example, has
// not been reloaded while this process is (long) running.
$q = $this->em->createQueryBuilder()
->from(Event::class, 'e')
->select('e')
->andWhere('e.eventid > :lastIdSent')
->andWhere('e.eventid >= :lastIdSent')
->setParameter('lastIdSent', $lastIdSent)
->orderBy('e.eventid', 'ASC')
->getQuery();

/** @var Event[] $events */
$events = $q->getResult();

if ($lastIdExists) {
if (count($events) == 0 || $events[0]->getEventid() !== $lastIdSent) {
throw new HttpException(500, sprintf('Cannot find event %d in database anymore', $lastIdSent));
}
// Remove the previously last sent event. We just fetched
// it to make sure it's there.
unset($events[0]);
}

// Look for any missing sequential events and wait for them to
// be committed if so.
$missingEvents = false;
Expand Down Expand Up @@ -860,6 +874,7 @@ public function getEventFeedAction(
flush();
$lastUpdate = Utils::now();
$lastIdSent = $event->getEventid();
$lastIdExists = true;
$numEventsSent++;

if ($missingEvents && $event->getEventid() >= $lastFoundId) {
Expand Down

0 comments on commit 0f9a160

Please sign in to comment.