修复vjudge账号调度锁失效的bug

This commit is contained in:
Himit_ZH 2021-09-24 20:26:07 +08:00
parent 1d4f3ab9a2
commit 70cc10add1
6 changed files with 49 additions and 24 deletions

View File

@ -62,6 +62,12 @@ public class ChooseServer {
.eq("is_remote", isRemote)
.orderByAsc("task_number")
.last("for update"); // 开启悲观锁
/**
* 如果一个条件无法通过索引快速过滤存储引擎层面就会将所有记录加锁后返回
* 再由MySQL Server层进行过滤但在实际使用过程当中MySQL做了一些改进
* 在MySQL Server过滤条件发现不满足后会调用unlock_row方法
* 把不满足条件的记录释放锁 (违背了二段锁协议的约束)
*/
List<JudgeServer> judgeServerList = judgeServerService.list(judgeServerQueryWrapper);
// 获取可用判题机
for (JudgeServer judgeServer : judgeServerList) {

View File

@ -4,17 +4,21 @@ import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.hcode.hoj.judge.Dispatcher;
import top.hcode.hoj.pojo.entity.Judge;
import top.hcode.hoj.pojo.entity.RemoteJudgeAccount;
import top.hcode.hoj.pojo.entity.ToJudge;
import top.hcode.hoj.service.impl.JudgeServiceImpl;
import top.hcode.hoj.service.impl.ProblemServiceImpl;
import top.hcode.hoj.service.impl.RemoteJudgeAccountServiceImpl;
import top.hcode.hoj.utils.Constants;
import top.hcode.hoj.utils.RedisUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -37,6 +41,9 @@ public class RemoteJudgeReceiver {
@Autowired
private RemoteJudgeAccountServiceImpl remoteJudgeAccountService;
@Resource
private ApplicationContext applicationContext;
@Async("judgeTaskAsyncPool")
public void processWaitingTask() {
// 如果队列中还有任务则继续处理
@ -44,23 +51,33 @@ public class RemoteJudgeReceiver {
String taskJsonStr = (String) redisUtils.lrPop(Constants.Judge.STATUS_REMOTE_JUDGE_WAITING_HANDLE.getName());
// 再次检查
if (taskJsonStr != null) {
handleJudgeMsg(taskJsonStr);
JSONObject task = JSONUtil.parseObj(taskJsonStr);
Judge judge = task.get("judge", Judge.class);
String token = task.getStr("token");
String remoteJudgeProblem = task.getStr("remoteJudgeProblem");
Boolean isContest = task.getBool("isContest");
Integer tryAgainNum = task.getInt("tryAgainNum");
Boolean isHasSubmitIdRemoteReJudge = task.getBool("isHasSubmitIdRemoteReJudge");
String remoteOJName = remoteJudgeProblem.split("-")[0].toUpperCase();
applicationContext.getBean(RemoteJudgeReceiver.class)
.handleJudgeMsg(judge,
token,
remoteJudgeProblem,
isContest,
tryAgainNum,
isHasSubmitIdRemoteReJudge,
remoteOJName);
}
}
}
public void handleJudgeMsg(String taskJsonStr) {
JSONObject task = JSONUtil.parseObj(taskJsonStr);
Judge judge = task.get("judge", Judge.class);
String token = task.getStr("token");
String remoteJudgeProblem = task.getStr("remoteJudgeProblem");
Boolean isContest = task.getBool("isContest");
Integer tryAgainNum = task.getInt("tryAgainNum");
Boolean isHasSubmitIdRemoteReJudge = task.getBool("isHasSubmitIdRemoteReJudge");
String remoteOJName = remoteJudgeProblem.split("-")[0].toUpperCase();
@Transactional
public void handleJudgeMsg(Judge judge, String token, String remoteJudgeProblem, Boolean isContest, Integer tryAgainNum,
Boolean isHasSubmitIdRemoteReJudge, String remoteOJName) {
// 过滤出当前远程oj可用的账号列表
QueryWrapper<RemoteJudgeAccount> remoteJudgeAccountQueryWrapper = new QueryWrapper<>();
@ -83,6 +100,7 @@ public class RemoteJudgeReceiver {
// POJ已有submitId的重判需要使用原来的账号获取结果
if (isNeedAccountRejudge) {
if (remoteJudgeAccount.getUsername().equals(judge.getVjudgeUsername())) {
judge.setVjudgePassword(remoteJudgeAccount.getPassword()); // 避免账号改密码
isHasAccountRejudge = true;
remoteJudgeAccount.setStatus(false);
boolean isOk = remoteJudgeAccountService.updateById(remoteJudgeAccount);

View File

@ -53,18 +53,18 @@ public class ConfigUtils {
" port: " + configVo.getRedisPort() + "\n" +
" password: " + configVo.getRedisPassword() + "\n" +
" web-config:\n" +
" base-url: " + UnicodeUtil.toUnicode(configVo.getBaseUrl()) + "\n" +
" name: " + UnicodeUtil.toUnicode(configVo.getName()) + "\n" +
" short-name: " + UnicodeUtil.toUnicode(configVo.getShortName()) + "\n" +
" description: " + UnicodeUtil.toUnicode(configVo.getDescription(),true) + "\n" +
" base-url: \"" + UnicodeUtil.toUnicode(configVo.getBaseUrl()) + "\"\n" +
" name: \"" + UnicodeUtil.toUnicode(configVo.getName()) + "\"\n" +
" short-name: \"" + UnicodeUtil.toUnicode(configVo.getShortName()) + "\"\n" +
" description: \"" + UnicodeUtil.toUnicode(configVo.getDescription(),true) + "\"\n" +
" register: " + configVo.getRegister() + "\n" +
" footer:\n" +
" record:\n" +
" name: " + UnicodeUtil.toUnicode(configVo.getRecordName()) + "\n" +
" url: " + UnicodeUtil.toUnicode(configVo.getRecordUrl()) + "\n" +
" name: \"" + UnicodeUtil.toUnicode(configVo.getRecordName()) + "\"\n" +
" url: \"" + UnicodeUtil.toUnicode(configVo.getRecordUrl()) + "\"\n" +
" project:\n" +
" name: " + UnicodeUtil.toUnicode(configVo.getProjectName()) + "\n" +
" url: " + UnicodeUtil.toUnicode(configVo.getProjectUrl()) + "\n" +
" name: \"" + UnicodeUtil.toUnicode(configVo.getProjectName()) + "\"\n" +
" url: \"" + UnicodeUtil.toUnicode(configVo.getProjectUrl()) + "\"\n" +
" hdu:\n" +
" account:\n" +
" username: " + listToStr(configVo.getHduUsernameList()) + "\n" +

View File

@ -170,6 +170,7 @@ public class HduJudge implements RemoteJudgeStrategy {
put("Queuing", Constants.Judge.STATUS_PENDING);
put("Running", Constants.Judge.STATUS_JUDGING);
put("Compiling", Constants.Judge.STATUS_COMPILING);
put("Runtime Error", Constants.Judge.STATUS_RUNTIME_ERROR);
put("Time Limit Exceeded", Constants.Judge.STATUS_TIME_LIMIT_EXCEEDED);
put("Memory Limit Exceeded", Constants.Judge.STATUS_MEMORY_LIMIT_EXCEEDED);
put("Output Limit Exceeded", Constants.Judge.STATUS_RUNTIME_ERROR);

View File

@ -62,8 +62,8 @@ export const m = {
Delete_User:'删除用户',
Import_User: '导入用户',
Import_User_Tips1:'用户数据导入仅支持csv格式的用户数据。',
Import_User_Tips2:'共列数据:用户名和密码不能为空,邮箱和真实姓名可选填,否则该行数据可能导入失败。',
Import_User_Tips3:'第一行不必写(“用户名”,“密码”,“邮箱”,"真实姓名")这个列名',
Import_User_Tips2:'共列数据:用户名和密码不能为空,邮箱和真实姓名可选填,否则该行数据可能导入失败。',
Import_User_Tips3:'第一行不必写(“用户名”,“密码”,“邮箱”,"真实姓名")这个列名',
Import_User_Tips4:'请导入保存为UTF-8编码的文件否则中文可能会乱码。',
Choose_File:'选择文件',
Password: '密码',

View File

@ -57,7 +57,7 @@ module.exports={
port: 8088, // 开发服务器运行端口号
proxy: {
'/api': { // 以'/api'开头的请求会被代理进行转发
target: 'http://localhost:6688', // 要发向的后台服务器地址 如果后台服务跑在后台开发人员的机器上,就写成 `http://ip:port` 如 `http:192.168.12.213:8081` ip为后台服务器的ip
target: 'https://hdoi.cn', // 要发向的后台服务器地址 如果后台服务跑在后台开发人员的机器上,就写成 `http://ip:port` 如 `http:192.168.12.213:8081` ip为后台服务器的ip
changeOrigin: true
}
},