-
Notifications
You must be signed in to change notification settings - Fork 271
Removes unneeded transactions. #2909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tuupke
wants to merge
4
commits into
DOMjudge:main
Choose a base branch
from
tuupke:Feature/remove-transactions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+116
−66
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ | |
use Doctrine\DBAL\ArrayParameterType; | ||
use Doctrine\DBAL\Exception; | ||
use Doctrine\DBAL\Exception as DBALException; | ||
use Doctrine\DBAL\TransactionIsolationLevel; | ||
use Doctrine\ORM\AbstractQuery; | ||
use Doctrine\ORM\EntityManager; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
|
@@ -313,33 +314,89 @@ public function updateJudgingAction( | |
} | ||
|
||
if ($request->request->has('output_compile')) { | ||
$output_compile = base64_decode($request->request->get('output_compile')); | ||
|
||
// Note: we use ->get here instead of ->has since entry_point can be the empty string and then we do not | ||
// want to update the submission or send out an update event | ||
if ($request->request->get('entry_point')) { | ||
$this->em->wrapInTransaction(function () use ($query, $request, &$judging) { | ||
$submission = $judging->getSubmission(); | ||
if ($submission->getEntryPoint() === $request->request->get('entry_point')) { | ||
return; | ||
// Lock-free setting of, and detection of mismatched entry_point. | ||
$submission = $judging->getSubmission(); | ||
|
||
// Retrieve, and update the current entrypoint. | ||
$oldEntryPoint = $submission->getEntryPoint(); | ||
$newEntryPoint = $request->request->get('entry_point'); | ||
|
||
|
||
if ($oldEntryPoint === $newEntryPoint) { | ||
// Nothing to do | ||
} elseif (!empty($oldEntryPoint)) { | ||
// Conflict detected disable the judgehost. | ||
$disabled = [ | ||
'kind' => 'judgehost', | ||
'hostname' => $judgehost->getHostname(), | ||
]; | ||
$error = new InternalError(); | ||
$error | ||
->setJudging($judging) | ||
->setContest($judging->getContest()) | ||
->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".') | ||
->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile)) | ||
->setTime(Utils::now()) | ||
->setDisabled($disabled); | ||
$this->em->persist($error); | ||
} else { | ||
// Update needed. Note, conflicts might still be possible. | ||
|
||
$rowsAffected = $this->em->createQueryBuilder() | ||
->update(Submission::class, 's') | ||
->set('s.entry_point', ':entrypoint') | ||
->andWhere('s.submitid = :id') | ||
->andWhere('s.entry_point IS NULL') | ||
->setParameter('entrypoint', $newEntryPoint) | ||
->setParameter('id', $submission->getSubmitid()) | ||
->getQuery() | ||
->execute(); | ||
|
||
if ($rowsAffected == 0) { | ||
// There is a potential conflict, two options. | ||
// The new entry point is either the same (no issue) or different (conflict). | ||
// Read the entrypoint and check. | ||
$this->em->clear(); | ||
$currentEntryPoint = $query->getOneOrNullResult()->getSubmission()->getEntryPoint(); | ||
if ($newEntryPoint !== $currentEntryPoint) { | ||
// Conflict detected disable the judgehost. | ||
$disabled = [ | ||
'kind' => 'judgehost', | ||
'hostname' => $judgehost->getHostname(), | ||
]; | ||
$error = new InternalError(); | ||
$error | ||
->setJudging($judging) | ||
->setContest($judging->getContest()) | ||
->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".') | ||
->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile)) | ||
->setTime(Utils::now()) | ||
->setDisabled($disabled); | ||
$this->em->persist($error); | ||
} | ||
} else { | ||
$submissionId = $submission->getSubmitid(); | ||
$contestId = $submission->getContest()->getCid(); | ||
$this->eventLogService->log('submission', $submissionId, | ||
EventLogService::ACTION_UPDATE, $contestId); | ||
} | ||
$submission->setEntryPoint($request->request->get('entry_point')); | ||
$this->em->flush(); | ||
$submissionId = $submission->getSubmitid(); | ||
$contestId = $submission->getContest()->getCid(); | ||
$this->eventLogService->log('submission', $submissionId, | ||
EventLogService::ACTION_UPDATE, $contestId); | ||
|
||
// As EventLogService::log() will clear the entity manager, so the judging has | ||
// now become detached. We will have to reload it. | ||
// As EventLogService::log() will clear the entity manager, both branches clear the entity manager. | ||
// The judging is now detached, reload it. | ||
/** @var Judging $judging */ | ||
$judging = $query->getOneOrNullResult(); | ||
}); | ||
} | ||
} | ||
|
||
// Reload judgehost just in case it got cleared above. | ||
/** @var Judgehost $judgehost */ | ||
$judgehost = $this->em->getRepository(Judgehost::class)->findOneBy(['hostname' => $hostname]); | ||
|
||
$output_compile = base64_decode($request->request->get('output_compile')); | ||
if ($request->request->getBoolean('compile_success')) { | ||
if ($judging->getOutputCompile() === null) { | ||
$judging | ||
|
@@ -809,32 +866,24 @@ public function internalErrorAction(Request $request): ?int | |
|
||
if ($field_name !== null) { | ||
// Disable any outstanding judgetasks with the same script that have not been claimed yet. | ||
$this->em->wrapInTransaction(function (EntityManager $em) use ($field_name, $disabled_id, $error) { | ||
$judgingids = $em->getConnection()->executeQuery( | ||
'SELECT DISTINCT jobid' | ||
. ' FROM judgetask' | ||
. ' WHERE ' . $field_name . ' = :id' | ||
. ' AND judgehostid IS NULL' | ||
. ' AND valid = 1', | ||
[ | ||
'id' => $disabled_id, | ||
] | ||
)->fetchFirstColumn(); | ||
$judgings = $em->getRepository(Judging::class)->findBy(['judgingid' => $judgingids]); | ||
foreach ($judgings as $judging) { | ||
/** @var Judging $judging */ | ||
$judging->setInternalError($error); | ||
} | ||
$em->flush(); | ||
$em->getConnection()->executeStatement( | ||
'UPDATE judgetask SET valid=0' | ||
. ' WHERE ' . $field_name . ' = :id' | ||
. ' AND judgehostid IS NULL', | ||
[ | ||
'id' => $disabled_id, | ||
] | ||
); | ||
}); | ||
$rows = $this->em->createQueryBuilder() | ||
->update(Judging::class, 'j') | ||
->leftJoin(JudgeTask::class, 'jt') | ||
->set('j.internal_error', ':error') | ||
->set('jt.valid', 0) | ||
->andWhere('jt.' . $field_name . ' = :id') | ||
->andWhere('j.internal_error IS NULL') | ||
->andWhere('jt.judgehost_id IS NULL') | ||
->andWhere('jt.valid = 1') | ||
->setParameter('error', $error) | ||
->setParameter('id', $disabled_id) | ||
->distinct() | ||
->getQuery() | ||
->getArrayResult(); | ||
|
||
if ($rows == 0) { | ||
// TODO, handle this case. Nothing was updated. | ||
} | ||
} | ||
|
||
$this->dj->setInternalError($disabled, $contest, false); | ||
|
@@ -861,37 +910,22 @@ public function internalErrorAction(Request $request): ?int | |
*/ | ||
protected function giveBackJudging(int $judgingId, ?Judgehost $judgehost): void | ||
{ | ||
// Reset the judgings without using Doctrine, it has no support for update queries containing a join. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doctrine 🤯 |
||
// Both databases supported by DOMjudge (MariaDB, MySQL) support these types of queries. | ||
$judging = $this->em->getRepository(Judging::class)->find($judgingId); | ||
if ($judging) { | ||
$this->em->wrapInTransaction(function () use ($judging, $judgehost) { | ||
/** @var JudgingRun $run */ | ||
foreach ($judging->getRuns() as $run) { | ||
if ($judgehost === null) { | ||
// This is coming from internal errors, reset the whole judging. | ||
$run->getJudgetask() | ||
->setValid(false); | ||
continue; | ||
} | ||
|
||
// We do not have to touch any finished runs | ||
if ($run->getRunresult() !== null) { | ||
continue; | ||
} | ||
|
||
// For the other runs, we need to reset the judge task if it belongs to the current judgehost. | ||
if ($run->getJudgetask()->getJudgehost() && $run->getJudgetask()->getJudgehost()->getHostname() === $judgehost->getHostname()) { | ||
$run->getJudgetask() | ||
->setJudgehost(null) | ||
->setStarttime(null); | ||
} | ||
} | ||
|
||
$this->em->flush(); | ||
}); | ||
|
||
if ($judgehost === null) { | ||
// Invalidate old judging and create a new one - but without judgetasks yet since this was triggered by | ||
// an internal error. | ||
$this->em->getConnection()->executeStatement( | ||
'UPDATE judging_run jr ' . | ||
'JOIN judgetask jt ON jt.judgetaskid = jr.judgetaskid ' . | ||
'SET jt.valid = 0 ' . | ||
'WHERE jr.judgingid = :judgingid', | ||
[ | ||
'judgingid' => $judgingId, | ||
]); | ||
|
||
$judging->setValid(false); | ||
$newJudging = new Judging(); | ||
$newJudging | ||
|
@@ -901,6 +935,19 @@ protected function giveBackJudging(int $judgingId, ?Judgehost $judgehost): void | |
->setOriginalJudging($judging); | ||
$this->em->persist($newJudging); | ||
$this->em->flush(); | ||
} else { | ||
// Hand back the non-completed work of this judgehost within this judgetask. | ||
$this->em->getConnection()->executeStatement( | ||
'UPDATE judging_run jr ' . | ||
'JOIN judgetask jt ON jt.judgetaskid = jr.judgetaskid ' . | ||
'SET jt.judgehostid = null, jt.starttime = null ' . | ||
'WHERE jr.judgingid = :judgingid ' . | ||
' AND jr.runresult IS NOT NULL ' . | ||
' AND jt.judgehostid = :judgehost', | ||
[ | ||
'judgingid' => $judgingId, | ||
'judgehost' => $judgehost->getJudgehostid(), | ||
]); | ||
} | ||
|
||
$this->dj->auditlog('judging', $judgingId, 'given back' | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything to be handled? If there is no outstanding that we would disable here, we don't have an issue, do we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point, I'll remove.
Reading this back, this cannot be correct! I've had to create a manual query for updates with joins. 🤯
Apparently this case is never hit in the testsuite either.