Skip to content

Commit 9580582

Browse files
committed
Implement analyst evaluation mode
The idea behind this mode is that we are not really shadowing and trust the event feed results. However, we want to judge "interesting" runs locally to get useful information without the judging capacity to judge all testcases due to limited judgehost assignment. We do not consider 'TLE' or 'AC' interesting, as rerunning will not yield much more information. We consider 'WA' very interesting and prioritize the judging, but allow manual judging to overtake the priority. We consider 'CE' somewhat interesting, but downprioritize them a lot. For other verdicts, keep the normal priority.
1 parent 5c5b651 commit 9580582

File tree

6 files changed

+66
-11
lines changed

6 files changed

+66
-11
lines changed

etc/db-config.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,13 @@
118118
type: int
119119
default_value: 1
120120
public: false
121-
description: Lazy evaluation of results? If enabled, stops judging as soon as a highest priority result is found, otherwise always all testcases will be judged. On request will not auto-start judging and is typically used when running as analyst system.
121+
description: Lazy evaluation of results? If enabled, stops judging as soon as a highest priority result is found, otherwise always all testcases will be judged. On request will not auto-start judging. Analyst mode tries to judge only interesting testcases.
122122
options:
123123
1: Lazy
124124
2: Full judging
125125
3: Only on request
126-
regex: /^[123]$/
126+
4: Analyst mode
127+
regex: /^[1234]$/
127128
error_message: A value between 1 and 3 is required.
128129
- name: judgehost_warning
129130
type: int

webapp/src/Controller/API/JudgehostController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,9 @@ private function addSingleJudgingRun(
10831083
throw new BadMethodCallException('internal bug: the evaluated result changed during judging');
10841084
}
10851085

1086-
if ($lazyEval !== DOMJudgeService::EVAL_FULL) {
1086+
if ($$lazyEval === DOMJudgeService::EVAL_ANALYST) {
1087+
// Explicitly do not update priorities or cancel activated tasks.
1088+
} elseif ($lazyEval !== DOMJudgeService::EVAL_FULL) {
10871089
// We don't want to continue on this problem, even if there's spare resources.
10881090
$this->em->getConnection()->executeStatement(
10891091
'UPDATE judgetask SET valid=0, priority=:priority'

webapp/src/Service/DOMJudgeService.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class DOMJudgeService
7474
final public const EVAL_LAZY = 1;
7575
final public const EVAL_FULL = 2;
7676
final public const EVAL_DEMAND = 3;
77+
final public const EVAL_ANALYST = 4;
7778

7879
// Regex external identifiers must adhere to. Note that we are not checking whether it
7980
// does not start with a dot or dash or ends with a dot. We could but it would make the
@@ -1184,7 +1185,7 @@ public function unblockJudgeTasks(): void
11841185
}
11851186
}
11861187

1187-
public function maybeCreateJudgeTasks(Judging $judging, int $priority = JudgeTask::PRIORITY_DEFAULT, bool $manualRequest = false, int $overshoot = 0): void
1188+
public function maybeCreateJudgeTasks(Judging $judging, int $priority = JudgeTask::PRIORITY_DEFAULT, bool $manualRequest = false, int $overshoot = 0, bool $valid = true): void
11881189
{
11891190
$submission = $judging->getSubmission();
11901191
$problem = $submission->getContestProblem();
@@ -1197,7 +1198,7 @@ public function maybeCreateJudgeTasks(Judging $judging, int $priority = JudgeTas
11971198
return;
11981199
}
11991200

1200-
$this->actuallyCreateJudgetasks($priority, $judging, $overshoot);
1201+
$this->actuallyCreateJudgetasks($priority, $judging, $overshoot, $valid);
12011202

12021203
$team = $submission->getTeam();
12031204
$result = $this->em->createQueryBuilder()
@@ -1215,7 +1216,7 @@ public function maybeCreateJudgeTasks(Judging $judging, int $priority = JudgeTas
12151216

12161217
// Teams that submit frequently slow down the judge queue but should not be able to starve other teams of their
12171218
// deserved and timely judgement.
1218-
// For every "recent" pending job in the queue by that team, add a penalty (60s). Our definiition of "recent"
1219+
// For every "recent" pending job in the queue by that team, add a penalty (60s). Our definition of "recent"
12191220
// includes all submissions that have been placed at a virtual time (including penalty) more recent than 60s
12201221
// ago. This is done in order to avoid punishing teams who submit while their submissions are stuck in the queue
12211222
// for other reasons, for example an internal error for a problem or language.
@@ -1586,19 +1587,20 @@ private function allowJudge(ContestProblem $problem, Submission $submission, Lan
15861587
return !$evalOnDemand;
15871588
}
15881589

1589-
private function actuallyCreateJudgetasks(int $priority, Judging $judging, int $overshoot = 0): void
1590+
private function actuallyCreateJudgetasks(int $priority, Judging $judging, int $overshoot = 0, bool $valid = true): void
15901591
{
15911592
$submission = $judging->getSubmission();
15921593
$problem = $submission->getContestProblem();
15931594
// We use a mass insert query, since that is way faster than doing a separate insert for each testcase.
1594-
// We first insert judgetasks, then select their ID's and finally insert the judging runs.
1595+
// We first insert judgetasks, then select their IDs and finally insert the judging runs.
15951596

15961597
// Step 1: Create the template for the judgetasks.
15971598
$compileExecutable = $submission->getLanguage()->getCompileExecutable()->getImmutableExecutable();
15981599
$judgetaskInsertParams = [
15991600
':type' => JudgeTaskType::JUDGING_RUN,
16001601
':submitid' => $submission->getSubmitid(),
16011602
':priority' => $priority,
1603+
':valid' => $valid ? 1 : 0,
16021604
':jobid' => $judging->getJudgingid(),
16031605
':uuid' => $judging->getUuid(),
16041606
':compile_script_id' => $compileExecutable->getImmutableExecId(),

webapp/src/Service/ExternalContestSourceService.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use App\Entity\ExternalJudgement;
2828
use App\Entity\ExternalRun;
2929
use App\Entity\ExternalSourceWarning;
30+
use App\Entity\JudgeTask;
3031
use App\Entity\Language;
3132
use App\Entity\Problem;
3233
use App\Entity\Submission;
@@ -1777,13 +1778,13 @@ protected function importRun(Event $event, EventData $data): void
17771778
}
17781779

17791780
// First, load the external run.
1781+
$persist = false;
17801782
$run = $this->em
17811783
->getRepository(ExternalRun::class)
17821784
->findOneBy([
17831785
'contest' => $this->getSourceContest(),
17841786
'externalid' => $runId,
17851787
]);
1786-
$persist = false;
17871788
if (!$run) {
17881789
$run = new ExternalRun();
17891790
$run
@@ -1858,9 +1859,50 @@ protected function importRun(Event $event, EventData $data): void
18581859
if ($persist) {
18591860
$this->em->persist($run);
18601861
}
1862+
1863+
$lazyEval = $this->config->get('lazy_eval_results');
1864+
if ($lazyEval === DOMJudgeService::EVAL_ANALYST) {
1865+
// Check if we want to judge this testcase locally to provide useful information for analysts
1866+
$priority = $this->getAnalystRunPriority($run);
1867+
if ($priority !== null) {
1868+
// Make the judgetask valid and assign running priority if no judgehost has picked it up yet.
1869+
$this->em->createQueryBuilder()
1870+
->update(JudgeTask::class, 'jt')
1871+
->set('jt.valid', true)
1872+
->set('jt.priority', $priority)
1873+
->andWhere('jt.testcase_id = :testcase_id')
1874+
->andWhere('jt.submission = :submission')
1875+
->andWhere('jt.judgehost IS NULL')
1876+
->setParameter('testcase_id', $testcase->getTestcaseid())
1877+
->setParameter('submission', $externalJudgement->getSubmission())
1878+
->getQuery()
1879+
->execute();
1880+
}
1881+
}
1882+
18611883
$this->em->flush();
18621884
}
18631885

1886+
/**
1887+
* Checks if this run is interesting to judge locally for more analysis results.
1888+
* @param ExternalRun $run
1889+
* @return int The judging priority if it should be run locally, null otherwise.
1890+
*/
1891+
protected function getAnalystRunPriority(ExternalRun $run): int | null {
1892+
return match ($run->getResult()) {
1893+
// We will not get any new useful information for TLE testcases, while they take a lot of judgedaemon time.
1894+
'timelimit' => null,
1895+
// We often do not get new useful information for judging correct testcases.
1896+
'correct' => null,
1897+
// Wrong answers are interesting for the analysts, assign a high priority but below manual judging.
1898+
'wrong-answer' => -9,
1899+
// Compile errors could be interesting to see what went wrong, assign a low priority.
1900+
'compiler-error' => 9,
1901+
// Otherwise, judge with normal priority.
1902+
default => 0,
1903+
};
1904+
}
1905+
18641906
protected function processPendingEvents(string $type, string|int $id): void
18651907
{
18661908
// Process pending events.

webapp/src/Service/SubmissionService.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,15 @@ public function submitSolution(
732732
// This is so that we can use the submitid/judgingid below.
733733
$this->em->flush();
734734

735-
$this->dj->maybeCreateJudgeTasks($judging,
736-
$source === SubmissionSource::PROBLEM_IMPORT ? JudgeTask::PRIORITY_LOW : JudgeTask::PRIORITY_DEFAULT);
735+
$priority = match ($source) {
736+
SubmissionSource::PROBLEM_IMPORT => JudgeTask::PRIORITY_LOW,
737+
default => JudgeTask::PRIORITY_DEFAULT,
738+
};
739+
// Create judgetask as invalid when evaluating as analyst.
740+
$lazyEval = $this->config->get('lazy_eval_results');
741+
// We create invalid judgetasks, and only mark them valid when they are interesting for the analysts.
742+
$start_invalid = $lazyEval === DOMJudgeService::EVAL_ANALYST && $source == SubmissionSource::SHADOWING;
743+
$this->dj->maybeCreateJudgeTasks($judging, $priority, valid: !$start_invalid);
737744
}
738745

739746
$this->em->wrapInTransaction(function () use ($contest, $submission) {

webapp/tests/Unit/Integration/QueuetaskIntegrationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ protected function setUp(): void
6060
'shadow_mode' => 0,
6161
'sourcefiles_limit' => 1,
6262
'sourcesize_limit' => 1024*256,
63+
'lazy_eval_results' => 1,
6364
];
6465

6566
$this->config = $this->createMock(ConfigurationService::class);

0 commit comments

Comments
 (0)