core: seperate task and schedule to enhance performance (#440)

This commit is contained in:
undefined authored and GitHub committed 2022-10-16 01:13:21 +08:00
1 parent 101fcaa1b5
commit 175609259a
19 files changed
+250 -144

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js",
"version": "3.1.0",
"version": "3.1.1",
"main": "package.json",
"author": "undefined <i@undefined.moe>",
"repository": "https://github.com/hydro-dev/Hydro.git",
+6 -3
View File
@@ -2,9 +2,9 @@
/* eslint-disable no-await-in-loop */
import path from 'path';
import fs from 'fs-extra';
import type { JudgeResultBody } from 'hydrooj';
import {
SettingModel, StorageModel, SystemModel, TaskModel,
JudgeResultBody, RecordModel, SettingModel,
StorageModel, SystemModel, TaskModel,
} from 'hydrooj';
import { end, next } from 'hydrooj/src/handler/judge';
import { processTestdata } from '../cases';
@@ -75,7 +75,10 @@ const session = {
export async function postInit() {
if (SystemModel.get('hydrojudge.disable')) return;
const handle = (t) => (new JudgeTask(session, t)).handle().catch(logger.error);
const handle = async (t) => {
const rdoc = await RecordModel.get(t.domainId, t.rid);
(new JudgeTask(session, Object.assign(rdoc, t))).handle().catch(logger.error);
};
TaskModel.consume({ type: 'judge' }, handle);
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "4.0.18",
"version": "4.1.0",
"bin": "bin/hydrooj.js",
"main": "src/plugin-api",
"module": "src/plugin-api",
+10 -12
View File
@@ -17,9 +17,9 @@ import message from '../model/message';
import * as oplog from '../model/oplog';
import problem from '../model/problem';
import record from '../model/record';
import ScheduleModel from '../model/schedule';
import storage from '../model/storage';
import * as system from '../model/system';
import TaskModel from '../model/task';
import user from '../model/user';
import {
Handler, param, post, Types,
@@ -52,7 +52,7 @@ registerResolver(
'Get a contest by ID',
);
TaskModel.Worker.addHandler('contest', async (doc) => {
ScheduleModel.Worker.addHandler('contest', async (doc) => {
const tdoc = await contest.get(doc.domainId, doc.tid);
if (!tdoc) return;
const tasks = [];
@@ -194,7 +194,7 @@ export class ContestDetailHandler extends ContestDetailBaseHandler {
if (!this.user.own(this.tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST);
await contest.del(domainId, tid);
await record.updateMulti(domainId, { domainId, contest: tid }, undefined, undefined, { contest: '' });
await TaskModel.deleteMany({
await ScheduleModel.deleteMany({
type: 'schedule', subType: 'contest', domainId, tid,
});
this.response.redirect = this.url('contest_main');
@@ -341,20 +341,18 @@ export class ContestEditHandler extends Handler {
const task = {
type: 'schedule', subType: 'contest', domainId, tid,
};
await TaskModel.deleteMany(task);
await ScheduleModel.deleteMany(task);
const operation = [];
if (Date.now() <= endAt.getTime() && autoHide) {
// eslint-disable-next-line no-await-in-loop
await Promise.all(pids.map((pid) => problem.edit(domainId, pid, { hidden: true })));
await TaskModel.add({
...task,
operation: ['unhide'],
executeAfter: endAt,
});
operation.push('unhide');
}
if (lock && lockAt <= endAt) {
await TaskModel.add({
if (lock && lockAt <= endAt) operation.push('unlock');
if (operation.length) {
await ScheduleModel.add({
...task,
operation: ['unlock'],
operation,
executeAfter: endAt,
});
}
+4 -5
View File
@@ -103,9 +103,7 @@ export async function postJudge(rdoc: RecordDoc) {
contest: { $ne: new ObjectID('0'.repeat(24)) },
}).project({ _id: 1, contest: 1 }).toArray();
const priority = await record.submissionPriority(rdoc.uid, -5000 - rdocs.length * 5 - 50);
await Promise.all(rdocs.map(
(r) => record.judge(rdoc.domainId, r._id, priority, r.contest ? { detail: false } : {}, { hackRejudge: input }),
));
await record.judge(rdoc.domainId, rdocs.map((r) => r._id), priority, {}, { hackRejudge: input });
} catch (e) {
next({
rid: rdoc._id, domainId: rdoc.domainId, key: 'next', message: { message: 'Unable to apply hack: {0}', params: [e.message] },
@@ -202,10 +200,11 @@ class JudgeConnectionHandler extends ConnectionHandler {
// eslint-disable-next-line no-await-in-loop
if (!t) await sleep(500);
}
this.send({ task: t });
let rdoc = await record.get(t.domainId, t.rid);
this.send({ task: { ...rdoc, ...t } });
this.processing = t;
const $set = { status: builtin.STATUS.STATUS_FETCHED };
const rdoc = await record.update(t.domainId, t.rid, $set, {});
rdoc = await record.update(t.domainId, t.rid, $set, {});
bus.broadcast('record/change', rdoc, $set, {});
}
+5 -5
View File
@@ -439,17 +439,17 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
@param('pid', Types.UnsignedInt)
async postRejudge(domainId: string, pid: number) {
this.checkPerm(PERM.PERM_REJUDGE_PROBLEM);
// TODO maybe async?
const rdocs = await record.getMulti(domainId, {
pid,
contest: { $ne: new ObjectID('0'.repeat(24)) },
'files.hack': { $exists: false },
}).project({ _id: 1, contest: 1 }).toArray();
const priority = await record.submissionPriority(this.user._id, -10000 - rdocs.length * 5 - 50);
await Promise.all(rdocs.map(
(doc) => record.reset(domainId, doc._id, true)
.then(() => record.judge(domainId, doc._id, priority, doc.contest ? { detail: false } : {})),
));
await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
await Promise.all([
record.judge(domainId, rdocs.filter((i) => i.contest).map((i) => i._id), priority, { detail: false }),
record.judge(domainId, rdocs.filter((i) => !i.contest).map((i) => i._id), priority, {}),
]);
this.back();
}
+1 -4
View File
@@ -98,10 +98,7 @@ class RecordListHandler extends Handler {
filterStatus: status,
};
if (this.user.hasPriv(PRIV.PRIV_VIEW_JUDGE_STATISTICS) && !full) {
this.response.body.statistics = {
...await record.stat(all ? undefined : domainId),
delay: await TaskModel.getDelay({ type: 'judge' }),
};
this.response.body.statistics = await record.stat(all ? undefined : domainId);
}
}
}
+2 -2
View File
@@ -16,9 +16,9 @@ import domain from '../model/domain';
import oauth from '../model/oauth';
import * as oplog from '../model/oplog';
import problem, { ProblemDoc } from '../model/problem';
import ScheduleModel from '../model/schedule';
import SolutionModel from '../model/solution';
import * as system from '../model/system';
import task from '../model/task';
import token from '../model/token';
import user from '../model/user';
import {
@@ -376,7 +376,7 @@ class UserDetailHandler extends Handler {
class UserDeleteHandler extends Handler {
async post({ password }) {
this.user.checkPassword(password);
const tid = await task.add({
const tid = await ScheduleModel.add({
executeAfter: moment().add(7, 'days').toDate(),
type: 'script',
id: 'deleteUser',
+10 -1
View File
@@ -551,11 +551,18 @@ export interface Task {
_id: ObjectID;
type: string;
subType?: string;
executeAfter: Date;
priority: number;
[key: string]: any;
}
export interface Schedule {
_id: ObjectID;
type: string;
subType?: string;
executeAfter: Date;
[key: string]: any;
}
export interface FileNode {
/** File Path In S3 */
_id: string;
@@ -618,6 +625,7 @@ declare module './service/db' {
'oplog': OplogDoc;
'event': EventDoc;
'opcount': OpCountDoc;
'schedule': Schedule;
}
}
@@ -637,6 +645,7 @@ export interface Model {
solution: typeof import('./model/solution').default,
system: typeof import('./model/system'),
task: typeof import('./model/task').default,
schedule: typeof import('./model/schedule').default;
oplog: typeof import('./model/oplog'),
token: typeof import('./model/token').default,
training: typeof import('./model/training'),
+3 -3
View File
@@ -10,7 +10,7 @@ import { buildProjection } from '../utils';
import * as contest from './contest';
import * as document from './document';
import problem from './problem';
import TaskModel from './task';
import ScheduleModel from './schedule';
import * as training from './training';
export interface DiscussionDoc extends Document { }
@@ -294,8 +294,8 @@ async function updateSort() {
export function apply(ctx: Context) {
ctx.using(['worker'], async ({ worker }) => {
worker.addHandler('discussion.sort', updateSort);
if (!await TaskModel.count({ type: 'schedule', subType: 'discussion.sort' })) {
await TaskModel.add({
if (!await ScheduleModel.count({ type: 'schedule', subType: 'discussion.sort' })) {
await ScheduleModel.add({
type: 'schedule',
subType: 'discussion.sort',
executeAfter: moment().minute(0).second(0).millisecond(0).toDate(),
+19 -20
View File
@@ -1,5 +1,5 @@
/* eslint-disable object-curly-newline */
import { omit, sum } from 'lodash';
import { sum } from 'lodash';
import moment from 'moment-timezone';
import {
FilterQuery, MatchKeysAndValues,
@@ -9,7 +9,7 @@ import {
import { Context } from '../context';
import { ProblemNotFoundError } from '../error';
import {
FileInfo, JudgeMeta, JudgeRequest, ProblemConfigFile, RecordDoc,
FileInfo, JudgeMeta, ProblemConfigFile, RecordDoc,
} from '../interface';
import db from '../service/db';
import { MaybeArray, NumberKeys } from '../typeutils';
@@ -63,8 +63,10 @@ export default class RecordModel {
};
}
static async judge(domainId: string, rid: ObjectID, priority = 0, config: ProblemConfigFile = {}, meta: Partial<JudgeMeta> = {}) {
const rdoc = await RecordModel.get(domainId, rid);
static async judge(domainId: string, rids: MaybeArray<ObjectID>, priority = 0, config: ProblemConfigFile = {}, meta: Partial<JudgeMeta> = {}) {
rids = rids instanceof Array ? rids : [rids];
if (!rids.length) return null;
const rdoc = await RecordModel.get(domainId, rids[0]);
if (!rdoc) return null;
let data: FileInfo[] = [];
let source = `${domainId}/${rdoc.pid}`;
@@ -81,23 +83,20 @@ export default class RecordModel {
data = pdoc.data;
if (typeof pdoc.config === 'string') throw new Error(pdoc.config);
if (pdoc.config.type === 'remote_judge') {
if (rdoc.contest?.toHexString() !== '0'.repeat(24)) {
return await task.add({
...omit(rdoc, ['_id']),
priority,
type: 'remotejudge',
subType: pdoc.config.subType,
target: pdoc.config.target,
rid,
domainId,
config,
data,
});
}
await task.deleteMany({ rid: { $in: rids } });
return await task.addMany(rids.map((rid) => ({
...(pdoc.config as any),
priority,
type: 'remotejudge',
rid,
domainId,
config,
data,
} as any)));
}
}
return await task.add({
...omit(rdoc, ['_id']),
await task.deleteMany({ rid: { $in: rids } });
return await task.addMany(rids.map((rid) => ({
priority,
type: 'judge',
rid,
@@ -106,7 +105,7 @@ export default class RecordModel {
data,
source,
meta,
} as unknown as JudgeRequest);
} as any)));
}
static async add(
+162
View File
@@ -0,0 +1,162 @@
import moment from 'moment-timezone';
import { FilterQuery, ObjectID } from 'mongodb';
import { sleep } from '@hydrooj/utils/lib/utils';
import { Context, Service } from '../context';
import { Schedule } from '../interface';
import { Logger } from '../logger';
import * as bus from '../service/bus';
import db from '../service/db';
const logger = new Logger('model/schedule');
const coll = db.collection('schedule');
async function getFirst(query: FilterQuery<Schedule>) {
if (process.env.CI) return null;
const q = { ...query };
q.executeAfter = q.executeAfter || { $lt: new Date() };
const res = await coll.findOneAndDelete(q);
if (res.value) {
logger.debug('%o', res.value);
if (res.value.interval) {
const executeAfter = moment(res.value.executeAfter).add(...res.value.interval).toDate();
await coll.insertOne({ ...res.value, executeAfter });
}
return res.value;
}
return null;
}
class Consumer {
consuming: boolean;
running?: any;
constructor(public filter: any, public func: Function, public destoryOnError = true) {
this.consuming = true;
this.consume();
bus.on('app/exit', this.destory);
}
async consume() {
while (this.consuming) {
try {
// eslint-disable-next-line no-await-in-loop
const res = await getFirst(this.filter);
if (res) {
this.running = res;
// eslint-disable-next-line no-await-in-loop
await this.func(res);
this.running = null;
// eslint-disable-next-line no-await-in-loop
} else await sleep(1000);
} catch (err) {
logger.error(err);
if (this.destoryOnError) this.destory();
}
}
}
async destory() {
this.consuming = false;
}
}
class WorkerService extends Service {
private handlers: Record<string, Function> = {};
public consumer = new Consumer(
{ type: 'schedule', subType: { $in: Object.keys(this.handlers) } },
async (doc) => {
try {
logger.debug('Worker task: %o', doc);
const start = Date.now();
await Promise.race([
this.handlers[doc.subType](doc),
sleep(300000),
]);
const spent = Date.now() - start;
if (spent > 500) logger.warn('Slow worker task (%d ms): %s', spent, doc);
} catch (e) {
logger.error('Worker task fail: ', e);
logger.error('%o', doc);
}
},
false,
);
public addHandler(type: string, handler: Function) {
this.handlers[type] = handler;
this.consumer.filter = { type: 'schedule', subType: { $in: Object.keys(this.handlers) } };
this.caller?.on('dispose', () => {
delete this.handlers[type];
});
}
}
const Worker = new WorkerService(app, 'worker', false);
class ScheduleModel {
static coll = coll;
static async add(task: Partial<Schedule> & { type: string }) {
const res = await coll.insertOne({
...task,
executeAfter: task.executeAfter || new Date(),
_id: new ObjectID(),
});
return res.insertedId;
}
static get(_id: ObjectID) {
return coll.findOne({ _id });
}
static count(query: FilterQuery<Schedule>) {
return coll.find(query).count();
}
static del(_id: ObjectID) {
return coll.deleteOne({ _id });
}
static deleteMany(query: FilterQuery<Schedule>) {
return coll.deleteMany(query);
}
static getFirst = getFirst;
static Worker = Worker;
}
declare module '../context' {
interface Context {
worker: WorkerService;
}
}
export async function apply(ctx: Context) {
Context.service('worker', WorkerService);
ctx.worker = Worker;
Worker.addHandler('task.daily', async () => {
await global.Hydro.model.record.coll.deleteMany({ contest: new ObjectID('000000000000000000000000') });
await global.Hydro.script.rp?.run({}, new Logger('task/rp').debug);
await global.Hydro.script.problemStat?.run({}, new Logger('task/problem').debug);
if (global.Hydro.model.system.get('server.checkUpdate') && !(new Date().getDay() % 3)) {
await global.Hydro.script.checkUpdate?.run({}, new Logger('task/checkUpdate').debug);
}
await ctx.parallel('task/daily');
});
ctx.on('domain/delete', (domainId) => coll.deleteMany({ domainId }));
if (process.env.NODE_APP_INSTANCE !== '0') return;
if (!await ScheduleModel.count({ type: 'schedule', subType: 'task.daily' })) {
await ScheduleModel.add({
type: 'schedule',
subType: 'task.daily',
executeAfter: moment().add(1, 'day').hour(3).minute(0).second(0).millisecond(0).toDate(),
interval: [1, 'day'],
});
}
await db.ensureIndexes(coll, { name: 'schedule', key: { type: 1, subType: 1, executeAfter: -1 } });
}
export default ScheduleModel;
global.Hydro.model.schedule = ScheduleModel;
+4 -4
View File
@@ -7,8 +7,8 @@ import type { Readable } from 'stream';
import * as bus from '../service/bus';
import db from '../service/db';
import storage from '../service/storage';
import ScheduleModel from './schedule';
import * as system from './system';
import TaskModel from './task';
export class StorageModel {
static coll = db.collection('storage');
@@ -135,15 +135,15 @@ async function cleanFiles() {
res = await StorageModel.coll.findOneAndDelete({ autoDelete: { $lte: new Date() } });
}
}
TaskModel.Worker.addHandler('storage.prune', cleanFiles);
ScheduleModel.Worker.addHandler('storage.prune', cleanFiles);
bus.on('ready', async () => {
if (process.env.NODE_APP_INSTANCE !== '0') return;
await db.ensureIndexes(
StorageModel.coll,
{ key: { path: 1, autoDelete: 1 }, sparse: true, name: 'autoDelete' },
);
if (!await TaskModel.count({ type: 'schedule', subType: 'storage.prune' })) {
await TaskModel.add({
if (!await ScheduleModel.count({ type: 'schedule', subType: 'storage.prune' })) {
await ScheduleModel.add({
type: 'schedule',
subType: 'storage.prune',
executeAfter: moment().startOf('hour').toDate(),
+9 -78
View File
@@ -1,9 +1,8 @@
import { hostname } from 'os';
import moment from 'moment-timezone';
import { FilterQuery, ObjectID } from 'mongodb';
import { nanoid } from 'nanoid';
import { sleep } from '@hydrooj/utils/lib/utils';
import { Context, Service } from '../context';
import { Context } from '../context';
import { EventDoc, Task } from '../interface';
import { Logger } from '../logger';
import * as bus from '../service/bus';
@@ -16,14 +15,9 @@ const collEvent = db.collection('event');
async function getFirst(query: FilterQuery<Task>) {
if (process.env.CI) return null;
const q = { ...query };
q.executeAfter = q.executeAfter || { $lt: new Date() };
const res = await coll.findOneAndDelete(q, { sort: { priority: -1 } });
if (res.value) {
logger.debug('%o', res.value);
if (res.value.interval) {
const executeAfter = moment(res.value.executeAfter).add(...res.value.interval).toDate();
await coll.insertOne({ ...res.value, executeAfter });
}
return res.value;
}
return null;
@@ -63,51 +57,24 @@ class Consumer {
}
}
class WorkerService extends Service {
private handlers: Record<string, Function> = {};
public consumer = new Consumer(
{ type: 'schedule', subType: { $in: Object.keys(this.handlers) } },
async (doc) => {
try {
logger.debug('Worker task: %o', doc);
const start = Date.now();
await Promise.race([
this.handlers[doc.subType](doc),
sleep(300000),
]);
const spent = Date.now() - start;
if (spent > 500) logger.warn('Slow worker task (%d ms): %s', spent, doc);
} catch (e) {
logger.error('Worker task fail: ', e);
logger.error('%o', doc);
}
},
false,
);
public addHandler(type: string, handler: Function) {
this.handlers[type] = handler;
this.consumer.filter = { type: 'schedule', subType: { $in: Object.keys(this.handlers) } };
this.caller?.on('dispose', () => {
delete this.handlers[type];
});
}
}
const Worker = new WorkerService(app, 'worker', false);
class TaskModel {
static coll = coll;
static async add(task: Partial<Task> & { type: string }) {
const t: Task = {
...task,
priority: task.priority ?? 0,
executeAfter: task.executeAfter || new Date(),
_id: new ObjectID(),
};
const res = await coll.insertOne(t);
return res.insertedId;
}
static async addMany(tasks: Task[]) {
const res = await coll.insertMany(tasks);
return res.insertedIds;
}
static get(_id: ObjectID) {
return coll.findOne({ _id });
}
@@ -126,43 +93,14 @@ class TaskModel {
static getFirst = getFirst;
static async getDelay(query?: FilterQuery<Task>): Promise<[number, Date]> {
const now = new Date();
const [res] = await coll.find(query).sort({ executeAfter: 1 }).limit(1).toArray();
if (res) return [Math.max(0, now.getTime() - res.executeAfter.getTime()), res.executeAfter];
return [0, now];
}
static async consume(query: any, cb: Function, destoryOnError = true) {
return new Consumer(query, cb, destoryOnError);
}
static Consumer = Consumer;
static WorkerService = WorkerService;
static Worker = Worker;
}
const id = process.env.exec_mode === 'cluster_mode' ? hostname() : nanoid();
declare module '../context' {
interface Context {
worker: WorkerService;
}
}
export async function apply(ctx: Context) {
Context.service('worker', WorkerService);
ctx.worker = Worker;
Worker.addHandler('task.daily', async () => {
await global.Hydro.model.record.coll.deleteMany({ contest: new ObjectID('000000000000000000000000') });
await global.Hydro.script.rp?.run({}, new Logger('task/rp').debug);
await global.Hydro.script.problemStat?.run({}, new Logger('task/problem').debug);
if (global.Hydro.model.system.get('server.checkUpdate') && !(new Date().getDay() % 3)) {
await global.Hydro.script.checkUpdate?.run({}, new Logger('task/checkUpdate').debug);
}
await ctx.parallel('task/daily');
});
ctx.on('domain/delete', (domainId) => coll.deleteMany({ domainId }));
ctx.on('bus/broadcast', (event, payload) => {
collEvent.insertOne({
@@ -174,14 +112,6 @@ export async function apply(ctx: Context) {
});
if (process.env.NODE_APP_INSTANCE !== '0') return;
if (!await TaskModel.count({ type: 'schedule', subType: 'task.daily' })) {
await TaskModel.add({
type: 'schedule',
subType: 'task.daily',
executeAfter: moment().add(1, 'day').hour(3).minute(0).second(0).millisecond(0).toDate(),
interval: [1, 'day'],
});
}
await collEvent.createIndex({ expire: 1 }, { expireAfterSeconds: 0 });
const stream = collEvent.watch();
const handleEvent = async (doc: EventDoc) => {
@@ -208,6 +138,7 @@ export async function apply(ctx: Context) {
await (res.value ? handleEvent(res.value) : sleep(500));
}
});
await db.ensureIndexes(coll, { name: 'task', key: { type: 1, subType: 1, priority: -1 } });
}
export default TaskModel;
+8 -1
View File
@@ -15,6 +15,7 @@ import domain from './model/domain';
import MessageModel from './model/message';
import problem from './model/problem';
import RecordModel from './model/record';
import ScheduleModel from './model/schedule';
import * as system from './model/system';
import TaskModel from './model/task';
import user from './model/user';
@@ -395,7 +396,7 @@ const scripts: UpgradeScript[] = [
const _FRESH_INSTALL_IGNORE = 1;
const tasks = await db.collection('task').find({ type: 'schedule', subType: 'contest.problemHide' }).toArray();
for (const task of tasks) {
await TaskModel.add({ ...task, subType: 'contest', operation: ['unhide'] });
await ScheduleModel.add({ ...task, subType: 'contest', operation: ['unhide'] });
}
await TaskModel.deleteMany({ type: 'schedule', subType: 'contest.problemHide' });
return true;
@@ -563,6 +564,12 @@ const scripts: UpgradeScript[] = [
await db.collection('cache' as any).deleteMany({});
return true;
},
async function _69_70() {
const tasks = await TaskModel.coll.find({ type: 'schedule', subType: 'contest' }).toArray();
if (tasks.length) await ScheduleModel.coll.insertMany(tasks as any);
await TaskModel.coll.deleteMany({});
return true;
},
];
export default scripts;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
"version": "4.41.14",
"version": "4.41.15",
"author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0",
"main": "index.ts",
@@ -124,7 +124,6 @@
</div>
<div class="section__body">
<dl class="large horizontal">
<dt>{{ _('Delay')}}</dt><dd>{{ statistics.delay[0]|int }}</dd>
<dt>{{ _('5 Min')}}</dt><dd>{{ statistics['d5min'] }}</dd>
<dt>{{ _('1 Hour')}}</dt><dd>{{ statistics['d1h'] }}</dd>
<dt>{{ _('Day') }}</dt><dd>{{ statistics['day'] }}</dd>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/vjudge",
"version": "1.6.1",
"version": "1.6.2",
"description": "Submit problems to remote oj",
"main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git",
+3 -1
View File
@@ -2,7 +2,7 @@
import os from 'os';
import {
Context, db, DomainModel, JudgeHandler, Logger,
ProblemModel, sleep, STATUS, TaskModel, Time,
ProblemModel, RecordModel, sleep, STATUS, TaskModel, Time,
} from 'hydrooj';
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
import { getDifficulty } from './providers/codeforces';
@@ -35,6 +35,8 @@ class Service {
}
async judge(task) {
const rdoc = await RecordModel.get(task.domainId, task.rid);
task = Object.assign(rdoc, task);
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid });
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid });
await next({ status: STATUS.STATUS_FETCHED });