Skip to content

Commit 4045466

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

File tree

1 file changed

+32
-7
lines changed

1 file changed

+32
-7
lines changed

webapp/src/Controller/API/JudgehostController.php

Lines changed: 32 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,9 @@ 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', $newEntryPoint)
353+
->where('s.submitid = :id')
354+
->andWhere('s.entry_point IS NULL')
354355
->setParameter('id', $submission->getSubmitid())
355356
->getQuery()
356357
->execute();
@@ -1387,6 +1388,28 @@ public function checkVersions(Request $request, string $judgetaskid): array
13871388
if ($request->request->has('runner')) {
13881389
$reportedVersions['runner'] = base64_decode($request->request->get('runner'));
13891390
}
1391+
1392+
// We want the version update to be both lock-free manner while keeping one latest version exposed.
1393+
// To ensure this is the case a READ_UNCOMMITTED transaction is used. This is not ideal, but does keep both
1394+
// constraints. The updating threads might see multiple versions, but only the newest one is exposed.
1395+
//
1396+
// Keep in mind that there might be multiple concurrent updates while reading the explanation.
1397+
// - First insert a new version assuming it will be the latest version.
1398+
// - Retrieve all 'new versions', not newest 'new versions' stop processing.
1399+
// - The newest 'new version' assumes it is the latest version and updates all "older" 'new versions' to
1400+
// not be the latest.
1401+
// Age is determined by `judgetaskid`. The greatest `judgetaskid` is the newest. It might be possible that yet
1402+
// another thread is inserting versions. Though unfortunate there is no way to prevent this.
1403+
// Runs in a READ_UNCOMMITTED transaction to make sure concurrent threads see, and can update,
1404+
// required values.
1405+
$this->em->wrapInTransaction(function ($em) {
1406+
$isoLevel = $this->em->getConnection()->getTransactionIsolation();
1407+
$em->getConnection()->setTransactionIsolation(TransactionIsolationLevel::READ_UNCOMMITTED);
1408+
1409+
// Restore the isolation level.
1410+
$em->getConnection()->setTransactionIsolation($isoLevel);
1411+
});
1412+
13901413
$this->em->wrapInTransaction(function () use (
13911414
$judgehost,
13921415
$reportedVersions,
@@ -1396,10 +1419,12 @@ public function checkVersions(Request $request, string $judgetaskid): array
13961419
$activeVersion = $this->em->getRepository(Version::class)
13971420
->findOneBy(['language' => $language, 'judgehost' => $judgehost, 'active' => true]);
13981421

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

0 commit comments

Comments
 (0)