|
32 | 32 | use Doctrine\DBAL\ArrayParameterType;
|
33 | 33 | use Doctrine\DBAL\Exception;
|
34 | 34 | use Doctrine\DBAL\Exception as DBALException;
|
| 35 | +use Doctrine\DBAL\TransactionIsolationLevel; |
35 | 36 | use Doctrine\ORM\AbstractQuery;
|
36 | 37 | use Doctrine\ORM\EntityManager;
|
37 | 38 | use Doctrine\ORM\EntityManagerInterface;
|
@@ -313,33 +314,89 @@ public function updateJudgingAction(
|
313 | 314 | }
|
314 | 315 |
|
315 | 316 | if ($request->request->has('output_compile')) {
|
| 317 | + $output_compile = base64_decode($request->request->get('output_compile')); |
| 318 | + |
316 | 319 | // Note: we use ->get here instead of ->has since entry_point can be the empty string and then we do not
|
317 | 320 | // want to update the submission or send out an update event
|
318 | 321 | 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; |
| 322 | + // Lock-free setting of, and detection of mismatched entry_point. |
| 323 | + $submission = $judging->getSubmission(); |
| 324 | + |
| 325 | + // Retrieve, and update the current entrypoint. |
| 326 | + $oldEntryPoint = $submission->getEntryPoint(); |
| 327 | + $newEntryPoint = $request->request->get('entry_point'); |
| 328 | + |
| 329 | + |
| 330 | + if ($oldEntryPoint === $newEntryPoint) { |
| 331 | + // Nothing to do |
| 332 | + } elseif (!empty($oldEntryPoint)) { |
| 333 | + // Conflict detected disable the judgehost. |
| 334 | + $disabled = [ |
| 335 | + 'kind' => 'judgehost', |
| 336 | + 'hostname' => $judgehost->getHostname(), |
| 337 | + ]; |
| 338 | + $error = new InternalError(); |
| 339 | + $error |
| 340 | + ->setJudging($judging) |
| 341 | + ->setContest($judging->getContest()) |
| 342 | + ->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".') |
| 343 | + ->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile)) |
| 344 | + ->setTime(Utils::now()) |
| 345 | + ->setDisabled($disabled); |
| 346 | + $this->em->persist($error); |
| 347 | + } else { |
| 348 | + // Update needed. Note, conflicts might still be possible. |
| 349 | + |
| 350 | + $rowsAffected = $this->em->createQueryBuilder() |
| 351 | + ->update(Submission::class, 's') |
| 352 | + ->set('s.entry_point', ':entrypoint') |
| 353 | + ->andWhere('s.submitid = :id') |
| 354 | + ->andWhere('s.entry_point IS NULL') |
| 355 | + ->setParameter('entrypoint', $newEntryPoint) |
| 356 | + ->setParameter('id', $submission->getSubmitid()) |
| 357 | + ->getQuery() |
| 358 | + ->execute(); |
| 359 | + |
| 360 | + if ($rowsAffected == 0) { |
| 361 | + // There is a potential conflict, two options. |
| 362 | + // The new entry point is either the same (no issue) or different (conflict). |
| 363 | + // Read the entrypoint and check. |
| 364 | + $this->em->clear(); |
| 365 | + $currentEntryPoint = $query->getOneOrNullResult()->getSubmission()->getEntryPoint(); |
| 366 | + if ($newEntryPoint !== $currentEntryPoint) { |
| 367 | + // Conflict detected disable the judgehost. |
| 368 | + $disabled = [ |
| 369 | + 'kind' => 'judgehost', |
| 370 | + 'hostname' => $judgehost->getHostname(), |
| 371 | + ]; |
| 372 | + $error = new InternalError(); |
| 373 | + $error |
| 374 | + ->setJudging($judging) |
| 375 | + ->setContest($judging->getContest()) |
| 376 | + ->setDescription('Reported EntryPoint conflict difference for j' . $judging->getJudgingid().'. Expected: "' . $oldEntryPoint. '", received: "' . $newEntryPoint . '".') |
| 377 | + ->setJudgehostlog(base64_encode('New compilation output: ' . $output_compile)) |
| 378 | + ->setTime(Utils::now()) |
| 379 | + ->setDisabled($disabled); |
| 380 | + $this->em->persist($error); |
| 381 | + } |
| 382 | + } else { |
| 383 | + $submissionId = $submission->getSubmitid(); |
| 384 | + $contestId = $submission->getContest()->getCid(); |
| 385 | + $this->eventLogService->log('submission', $submissionId, |
| 386 | + EventLogService::ACTION_UPDATE, $contestId); |
323 | 387 | }
|
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); |
330 | 388 |
|
331 |
| - // As EventLogService::log() will clear the entity manager, so the judging has |
332 |
| - // now become detached. We will have to reload it. |
| 389 | + // As EventLogService::log() will clear the entity manager, both branches clear the entity manager. |
| 390 | + // The judging is now detached, reload it. |
333 | 391 | /** @var Judging $judging */
|
334 | 392 | $judging = $query->getOneOrNullResult();
|
335 |
| - }); |
| 393 | + } |
336 | 394 | }
|
337 | 395 |
|
338 | 396 | // Reload judgehost just in case it got cleared above.
|
339 | 397 | /** @var Judgehost $judgehost */
|
340 | 398 | $judgehost = $this->em->getRepository(Judgehost::class)->findOneBy(['hostname' => $hostname]);
|
341 | 399 |
|
342 |
| - $output_compile = base64_decode($request->request->get('output_compile')); |
343 | 400 | if ($request->request->getBoolean('compile_success')) {
|
344 | 401 | if ($judging->getOutputCompile() === null) {
|
345 | 402 | $judging
|
|
0 commit comments