Skip to content

Commit bbd1def

Browse files
committed
WIPWIPWIP: testing, will rebase.
1 parent 6303ace commit bbd1def

File tree

1 file changed

+33
-7
lines changed

1 file changed

+33
-7
lines changed

webapp/src/Controller/API/JudgehostController.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use Doctrine\DBAL\ArrayParameterType;
3333
use Doctrine\DBAL\Exception;
3434
use Doctrine\DBAL\Exception as DBALException;
35+
use Doctrine\DBAL\TransactionIsolationLevel;
3536
use Doctrine\ORM\AbstractQuery;
3637
use Doctrine\ORM\EntityManager;
3738
use Doctrine\ORM\EntityManagerInterface;
@@ -348,9 +349,10 @@ public function updateJudgingAction(
348349

349350
$rowsAffected = $this->em->createQueryBuilder()
350351
->update(Submission::class, 's')
351-
->set('entry_point', $newEntryPoint)
352-
->where('submitid = :id')
353-
->andWhere('entry_point IS NULL')
352+
->set('s.entry_point', ':entrypoint')
353+
->where('s.submitid = :id')
354+
->andWhere('s.entry_point IS NULL')
355+
->setParameter('entrypoint', $newEntryPoint)
354356
->setParameter('id', $submission->getSubmitid())
355357
->getQuery()
356358
->execute();
@@ -1387,6 +1389,28 @@ public function checkVersions(Request $request, string $judgetaskid): array
13871389
if ($request->request->has('runner')) {
13881390
$reportedVersions['runner'] = base64_decode($request->request->get('runner'));
13891391
}
1392+
1393+
// We want the version update to be both lock-free manner while keeping one latest version exposed.
1394+
// To ensure this is the case a READ_UNCOMMITTED transaction is used. This is not ideal, but does keep both
1395+
// constraints. The updating threads might see multiple versions, but only the newest one is exposed.
1396+
//
1397+
// Keep in mind that there might be multiple concurrent updates while reading the explanation.
1398+
// - First insert a new version assuming it will be the latest version.
1399+
// - Retrieve all 'new versions', not newest 'new versions' stop processing.
1400+
// - The newest 'new version' assumes it is the latest version and updates all "older" 'new versions' to
1401+
// not be the latest.
1402+
// Age is determined by `judgetaskid`. The greatest `judgetaskid` is the newest. It might be possible that yet
1403+
// another thread is inserting versions. Though unfortunate there is no way to prevent this.
1404+
// Runs in a READ_UNCOMMITTED transaction to make sure concurrent threads see, and can update,
1405+
// required values.
1406+
$this->em->wrapInTransaction(function ($em) {
1407+
$isoLevel = $this->em->getConnection()->getTransactionIsolation();
1408+
$em->getConnection()->setTransactionIsolation(TransactionIsolationLevel::READ_UNCOMMITTED);
1409+
1410+
// Restore the isolation level.
1411+
$em->getConnection()->setTransactionIsolation($isoLevel);
1412+
});
1413+
13901414
$this->em->wrapInTransaction(function () use (
13911415
$judgehost,
13921416
$reportedVersions,
@@ -1396,10 +1420,12 @@ public function checkVersions(Request $request, string $judgetaskid): array
13961420
$activeVersion = $this->em->getRepository(Version::class)
13971421
->findOneBy(['language' => $language, 'judgehost' => $judgehost, 'active' => true]);
13981422

1399-
$isNewVersion = false;
1400-
if (!$activeVersion) {
1401-
$isNewVersion = true;
1402-
} else {
1423+
$isNewVersion = !$activeVersion ||
1424+
$activeVersion->getCompilerVersion() !== @$reportedVersions['compiler'] ||
1425+
$activeVersion->getRunnerVersion() !== @$reportedVersions['runner'];
1426+
1427+
$isNewVersion = !$activeVersion;
1428+
if (!$isNewVersion) {
14031429
$reportedCompilerVersion = $reportedVersions['compiler'] ?? null;
14041430
if ($activeVersion->getCompilerVersion() !== $reportedCompilerVersion) {
14051431
$isNewVersion = true;

0 commit comments

Comments
 (0)