Skip to content

Commit ecb22f3

Browse files
committed
Lock, and transaction-free entry_point update
1 parent 8f7f672 commit ecb22f3

File tree

2 files changed

+89
-14
lines changed

2 files changed

+89
-14
lines changed

ifuckedup.sh

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
3+
4+
# Dump the database
5+
./bin/dj_setup_database -u root -p domjudge uninstall
6+
7+
# Import the database
8+
./bin/dj_setup_database -u root -p domjudge bare-install
9+
10+
# Clean the workdir
11+
sudo ./bin/dj_judgehost_cleanup all
12+
13+
sudo rm -rf output/judgings/*
14+
15+
# Import the problem
16+
cd example_problems
17+
yes | ../misc-tools/import-contest
18+
cd -
19+
20+
notify-send Done!

webapp/src/Controller/API/JudgehostController.php

+69-14
Original file line numberDiff line numberDiff line change
@@ -313,33 +313,88 @@ public function updateJudgingAction(
313313
}
314314

315315
if ($request->request->has('output_compile')) {
316+
$output_compile = base64_decode($request->request->get('output_compile'));
317+
316318
// Note: we use ->get here instead of ->has since entry_point can be the empty string and then we do not
317319
// want to update the submission or send out an update event
318320
if ($request->request->get('entry_point')) {
319-
$this->em->wrapInTransaction(function () use ($query, $request, &$judging) {
320-
$submission = $judging->getSubmission();
321-
if ($submission->getEntryPoint() === $request->request->get('entry_point')) {
322-
return;
321+
// Lock-free setting of, and detection of mismatched entry_point.
322+
$submission = $judging->getSubmission();
323+
324+
// Retrieve, and update the current entrypoint.
325+
$oldEntryPoint = $submission->getEntryPoint();
326+
$newEntryPoint = $request->request->get('entry_point');
327+
328+
329+
if ($oldEntryPoint === $newEntryPoint) {
330+
// Nothing to do
331+
} else if (!empty($oldEntryPoint)) {
332+
// Conflict detected disable the judgehost.
333+
$disabled = [
334+
'kind' => 'judgehost',
335+
'hostname' => $judgehost->getHostname(),
336+
];
337+
$error = new InternalError();
338+
$error
339+
->setJudging($judging)
340+
->setContest($judging->getContest())
341+
->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".')
342+
->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile))
343+
->setTime(Utils::now())
344+
->setDisabled($disabled);
345+
$this->em->persist($error);
346+
} else {
347+
// Update needed. Note, conflicts might still be possible.
348+
349+
$rowsAffected = $this->em->createQueryBuilder()
350+
->update(Submission::class, 's')
351+
->set('entry_point', $newEntryPoint)
352+
->where('submitid = :id')
353+
->andWhere('entry_point IS NULL')
354+
->setParameter('id', $submission->getSubmitid())
355+
->getQuery()
356+
->execute();
357+
358+
if ($rowsAffected == 0) {
359+
// There is a potential conflict, two options.
360+
// The new entry point is either the same (no issue) or different (conflict).
361+
// Read the entrypoint and check.
362+
$this->em->clear();
363+
$currentEntryPoint = $query->getOneOrNullResult()->getSubmission()->getEntryPoint();
364+
if ($newEntryPoint !== $currentEntryPoint) {
365+
// Conflict detected disable the judgehost.
366+
$disabled = [
367+
'kind' => 'judgehost',
368+
'hostname' => $judgehost->getHostname(),
369+
];
370+
$error = new InternalError();
371+
$error
372+
->setJudging($judging)
373+
->setContest($judging->getContest())
374+
->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".')
375+
->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile))
376+
->setTime(Utils::now())
377+
->setDisabled($disabled);
378+
$this->em->persist($error);
379+
}
380+
} else {
381+
$submissionId = $submission->getSubmitid();
382+
$contestId = $submission->getContest()->getCid();
383+
$this->eventLogService->log('submission', $submissionId,
384+
EventLogService::ACTION_UPDATE, $contestId);
323385
}
324-
$submission->setEntryPoint($request->request->get('entry_point'));
325-
$this->em->flush();
326-
$submissionId = $submission->getSubmitid();
327-
$contestId = $submission->getContest()->getCid();
328-
$this->eventLogService->log('submission', $submissionId,
329-
EventLogService::ACTION_UPDATE, $contestId);
330386

331-
// As EventLogService::log() will clear the entity manager, so the judging has
332-
// now become detached. We will have to reload it.
387+
// As EventLogService::log() will clear the entity manager, both branches clear the entity manager.
388+
// The judging is now detached, reload it.
333389
/** @var Judging $judging */
334390
$judging = $query->getOneOrNullResult();
335-
});
391+
}
336392
}
337393

338394
// Reload judgehost just in case it got cleared above.
339395
/** @var Judgehost $judgehost */
340396
$judgehost = $this->em->getRepository(Judgehost::class)->findOneBy(['hostname' => $hostname]);
341397

342-
$output_compile = base64_decode($request->request->get('output_compile'));
343398
if ($request->request->getBoolean('compile_success')) {
344399
if ($judging->getOutputCompile() === null) {
345400
$judging

0 commit comments

Comments
 (0)