judge: set higher priority for compile task

This commit is contained in:
undefined committed 2023-04-26 23:43:52 +08:00
1 parent 6d591aef37
commit 37851fc4ee
9 files changed
+31 -29

No files matched your search

+8 -8
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
import { STATUS } from '@hydrooj/utils/lib/status'; import { STATUS } from '@hydrooj/utils/lib/status';
import { FormatError, SystemError } from './error'; import { FormatError, SystemError } from './error';
import { CopyInFile, run } from './sandbox'; import { CopyInFile, runQueued } from './sandbox';
import { parse } from './testlib'; import { parse } from './testlib';
export interface CheckConfig { export interface CheckConfig {
@@ -54,7 +54,7 @@ function parseDiffMsg(msg: string) {
const checkers: Record<string, Checker> = new Proxy({ const checkers: Record<string, Checker> = new Proxy({
async default(config) { async default(config) {
const { stdout } = await run('/usr/bin/diff -BZ usrout answer', { const { stdout } = await runQueued('/usr/bin/diff -BZ usrout answer', {
copyIn: { copyIn: {
usrout: config.user_stdout, usrout: config.user_stdout,
answer: config.output, answer: config.output,
@@ -76,7 +76,7 @@ const checkers: Record<string, Checker> = new Proxy({
}, },
async strict(config) { async strict(config) {
const { stdout } = await run('/usr/bin/diff usrout answer', { const { stdout } = await runQueued('/usr/bin/diff usrout answer', {
copyIn: { copyIn: {
usrout: config.user_stdout, usrout: config.user_stdout,
answer: config.output, answer: config.output,
@@ -98,7 +98,7 @@ const checkers: Record<string, Checker> = new Proxy({
* exit code:返回判断结果 * exit code:返回判断结果
*/ */
async hustoj(config) { async hustoj(config) {
const { code, stdout } = await run(`${config.execute} input answer usrout`, { const { code, stdout } = await runQueued(`${config.execute} input answer usrout`, {
copyIn: { copyIn: {
usrout: config.user_stdout, usrout: config.user_stdout,
answer: config.output, answer: config.output,
@@ -123,7 +123,7 @@ const checkers: Record<string, Checker> = new Proxy({
* argv[6]:输出错误报告的文件 * argv[6]:输出错误报告的文件
*/ */
async lemon(config) { async lemon(config) {
const { files, code } = await run(`${config.execute} input usrout answer ${config.score} score message`, { const { files, code } = await runQueued(`${config.execute} input usrout answer ${config.score} score message`, {
copyIn: { copyIn: {
usrout: config.user_stdout, usrout: config.user_stdout,
answer: config.output, answer: config.output,
@@ -156,7 +156,7 @@ const checkers: Record<string, Checker> = new Proxy({
* exit code:返回判断结果 * exit code:返回判断结果
*/ */
async qduoj(config) { async qduoj(config) {
const { status, stdout } = await run(`${config.execute} input usrout`, { const { status, stdout } = await runQueued(`${config.execute} input usrout`, {
copyIn: { copyIn: {
usrout: config.user_stdout, usrout: config.user_stdout,
input: config.input, input: config.input,
@@ -182,7 +182,7 @@ const checkers: Record<string, Checker> = new Proxy({
*/ */
async syzoj(config) { async syzoj(config) {
// eslint-disable-next-line prefer-const // eslint-disable-next-line prefer-const
let { status, stdout, stderr } = await run(config.execute, { let { status, stdout, stderr } = await runQueued(config.execute, {
copyIn: { copyIn: {
input: config.input, input: config.input,
user_out: config.user_stdout, user_out: config.user_stdout,
@@ -198,7 +198,7 @@ const checkers: Record<string, Checker> = new Proxy({
}, },
async testlib(config) { async testlib(config) {
const { stderr, status, code } = await run(`${config.execute} /w/in /w/user_out /w/answer`, { const { stderr, status, code } = await runQueued(`${config.execute} /w/in /w/user_out /w/answer`, {
copyIn: { copyIn: {
in: config.input, in: config.input,
user_out: config.user_stdout, user_out: config.user_stdout,
+3 -2
View File
@@ -5,7 +5,7 @@ import checkers from './checkers';
import { CompileError, FormatError } from './error'; import { CompileError, FormatError } from './error';
import { Execute } from './interface'; import { Execute } from './interface';
import { import {
CopyIn, CopyInFile, del, run, CopyIn, CopyInFile, del, runQueued,
} from './sandbox'; } from './sandbox';
import { compilerText } from './utils'; import { compilerText } from './utils';
@@ -17,7 +17,7 @@ export default async function compile(
if (lang.compile) { if (lang.compile) {
const { const {
status, stdout, stderr, fileIds, status, stdout, stderr, fileIds,
} = await run( } = await runQueued(
copyIn['compile.sh'] ? '/bin/bash compile.sh' : lang.compile, copyIn['compile.sh'] ? '/bin/bash compile.sh' : lang.compile,
{ {
copyIn: { ...copyIn, [lang.code_file]: code }, copyIn: { ...copyIn, [lang.code_file]: code },
@@ -26,6 +26,7 @@ export default async function compile(
time: lang.compile_time_limit || 10000, time: lang.compile_time_limit || 10000,
memory: lang.compile_memory_limit || 256 * 1024 * 1024, memory: lang.compile_memory_limit || 256 * 1024 * 1024,
}, },
3,
); );
if (status === STATUS.STATUS_TIME_LIMIT_EXCEEDED) next?.({ message: 'Compile timeout.' }); if (status === STATUS.STATUS_TIME_LIMIT_EXCEEDED) next?.({ message: 'Compile timeout.' });
if (status === STATUS.STATUS_MEMORY_LIMIT_EXCEEDED) next?.({ message: 'Compile memory limit exceeded.' }); if (status === STATUS.STATUS_MEMORY_LIMIT_EXCEEDED) next?.({ message: 'Compile memory limit exceeded.' });
+1
View File
@@ -17,6 +17,7 @@ const JudgeSettings = Schema.object({
total_time_limit: Schema.number().default(60), total_time_limit: Schema.number().default(60),
processLimit: Schema.number().default(32), processLimit: Schema.number().default(32),
parallelism: Schema.number().default(2), parallelism: Schema.number().default(2),
concurrency: Schema.number(),
singleTaskParallelism: Schema.number().default(2), singleTaskParallelism: Schema.number().default(2),
rerun: Schema.number().description('Re-Run testcase if time-limit-exceeded (max per submission)').default(0), rerun: Schema.number().description('Re-Run testcase if time-limit-exceeded (max per submission)').default(0),
rate: Schema.number().default(1), rate: Schema.number().default(1),
+3 -2
View File
@@ -80,8 +80,9 @@ export async function postInit() {
logger.debug('Record not found: %o', t); logger.debug('Record not found: %o', t);
return; return;
} }
(new JudgeTask(session, Object.assign(rdoc, t))).handle().catch(logger.error); await (new JudgeTask(session, Object.assign(rdoc, t))).handle().catch(logger.error);
}; };
TaskModel.consume({ type: 'judge' }, handle); const parallelism = Math.max(getConfig('parallelism'), 2);
for (let i = 1; i < parallelism; i++) TaskModel.consume({ type: 'judge' }, handle);
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle); TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
} }
+3 -3
View File
@@ -5,7 +5,7 @@ import compile, { compileChecker } from '../compile';
import { runFlow } from '../flow'; import { runFlow } from '../flow';
import { Execute } from '../interface'; import { Execute } from '../interface';
import { Logger } from '../log'; import { Logger } from '../log';
import { del, run } from '../sandbox'; import { del, run, runQueued } from '../sandbox';
import signals from '../signals'; import signals from '../signals';
import { NormalizedCase } from '../utils'; import { NormalizedCase } from '../utils';
import { Context, ContextSubTask } from './interface'; import { Context, ContextSubTask } from './interface';
@@ -14,7 +14,7 @@ const logger = new Logger('judge/default');
function judgeCase(c: NormalizedCase) { function judgeCase(c: NormalizedCase) {
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => { return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
const res = await run( const res = await runQueued(
ctx.execute.execute, ctx.execute.execute,
{ {
stdin: { src: c.input }, stdin: { src: c.input },
@@ -63,7 +63,7 @@ function judgeCase(c: NormalizedCase) {
if (langConfig.analysis && !ctx.analysis) { if (langConfig.analysis && !ctx.analysis) {
ctx.analysis = true; ctx.analysis = true;
try { try {
const r = await run(langConfig.analysis, { const r = await runQueued(langConfig.analysis, {
copyIn: { copyIn: {
...ctx.execute.copyIn, ...ctx.execute.copyIn,
input: { src: c.input }, input: { src: c.input },
+4 -6
View File
@@ -5,7 +5,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
import checkers from '../checkers'; import checkers from '../checkers';
import compile, { compileChecker, compileValidator } from '../compile'; import compile, { compileChecker, compileValidator } from '../compile';
import { Execute } from '../interface'; import { Execute } from '../interface';
import { del, run } from '../sandbox'; import { del, run, runQueued } from '../sandbox';
import signals from '../signals'; import signals from '../signals';
import { parseMemoryMB, parseTimeMS } from '../utils'; import { parseMemoryMB, parseTimeMS } from '../utils';
import { Context } from './interface'; import { Context } from './interface';
@@ -28,7 +28,7 @@ export async function judge(ctx: Context) {
]); ]);
ctx.clean.push(() => fs.unlink(input)); ctx.clean.push(() => fs.unlink(input));
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 }); ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
const validateResult = await run( const validateResult = await runQueued(
validator.execute, validator.execute,
{ {
stdin: { src: input }, stdin: { src: input },
@@ -41,7 +41,7 @@ export async function judge(ctx: Context) {
const message = `${validateResult.stdout || ''}\n${validateResult.stderr || ''}`.trim(); const message = `${validateResult.stdout || ''}\n${validateResult.stderr || ''}`.trim();
return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message }); return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message });
} }
const res = await run( const res = await runQueued(
execute.execute, execute.execute,
{ {
stdin: { src: input }, stdin: { src: input },
@@ -76,9 +76,7 @@ export async function judge(ctx: Context) {
if (code < 32) message = signals[code]; if (code < 32) message = signals[code];
else message = { message: 'Your program returned {0}.', params: [code] }; else message = { message: 'Your program returned {0}.', params: [code] };
} }
await Promise.all( await Promise.allSettled(Object.values(res.fileIds).map((id) => del(id)));
Object.values(res.fileIds).map((id) => del(id)),
).catch(() => { /* Ignore file doesn't exist */ });
if (message) ctx.next({ message }); if (message) ctx.next({ message });
+4 -3
View File
@@ -2,7 +2,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
import compile from '../compile'; import compile from '../compile';
import { CompileError } from '../error'; import { CompileError } from '../error';
import { Logger } from '../log'; import { Logger } from '../log';
import { run } from '../sandbox'; import { runQueued } from '../sandbox';
import signals from '../signals'; import signals from '../signals';
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils'; import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
import { Context } from './interface'; import { Context } from './interface';
@@ -46,7 +46,7 @@ export const judge = async (ctx: Context) => {
} }
ctx.clean.push(ctx.execute.clean); ctx.clean.push(ctx.execute.clean);
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 }); ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
const res = await run( const res = await runQueued(
ctx.execute.execute, ctx.execute.execute,
{ {
stdin: { content: ctx.input }, stdin: { content: ctx.input },
@@ -55,6 +55,7 @@ export const judge = async (ctx: Context) => {
time: parseTimeMS(ctx.config.time || '1s') * 2, time: parseTimeMS(ctx.config.time || '1s') * 2,
memory: parseMemoryMB(ctx.config.memory || '128m'), memory: parseMemoryMB(ctx.config.memory || '128m'),
}, },
1,
); );
const { code, time, memory } = res; const { code, time, memory } = res;
let { status } = res; let { status } = res;
@@ -85,7 +86,7 @@ export const judge = async (ctx: Context) => {
if (langConfig.analysis) { if (langConfig.analysis) {
try { try {
ctx.analysis = true; ctx.analysis = true;
const r = await run(langConfig.analysis, { const r = await runQueued(langConfig.analysis, {
copyIn: { copyIn: {
...ctx.execute.copyIn, ...ctx.execute.copyIn,
input: { content: ctx.input }, input: { content: ctx.input },
@@ -4,7 +4,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
import checkers from '../checkers'; import checkers from '../checkers';
import { compileChecker } from '../compile'; import { compileChecker } from '../compile';
import { runFlow } from '../flow'; import { runFlow } from '../flow';
import { del, run } from '../sandbox'; import { del, runQueued } from '../sandbox';
import { NormalizedCase } from '../utils'; import { NormalizedCase } from '../utils';
import { Context } from './interface'; import { Context } from './interface';
@@ -20,7 +20,7 @@ function judgeCase(c: NormalizedCase) {
let score = 0; let score = 0;
const fileIds = []; const fileIds = [];
if (ctx.config.subType === 'multi') { if (ctx.config.subType === 'multi') {
const res = await run( const res = await runQueued(
'/usr/bin/unzip foo.zip', '/usr/bin/unzip foo.zip',
{ {
stdin: null, stdin: null,
@@ -54,7 +54,7 @@ function judgeCase(c: NormalizedCase) {
env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() }, env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() },
})); }));
} }
await Promise.all(fileIds.map(del)).catch(() => { /* Ignore file doesn't exist */ }); await Promise.allSettled(fileIds.map(del));
return { return {
id: c.id, id: c.id,
status, status,
+2 -2
View File
@@ -206,10 +206,10 @@ export async function run(execute: string, params?: Parameter): Promise<SandboxA
return await adaptResult(result, params); return await adaptResult(result, params);
} }
const queue = new PQueue({ concurrency: getConfig('parallelism') }); const queue = new PQueue({ concurrency: getConfig('concurrency') || getConfig('parallelism') });
export function runQueued(execute: string, params?: Parameter, priority = 0) { export function runQueued(execute: string, params?: Parameter, priority = 0) {
return queue.add(() => run(execute, params), { priority }); return queue.add(() => run(execute, params), { priority }) as Promise<SandboxAdaptedResult>;
} }
export * from './sandbox/interface'; export * from './sandbox/interface';