Bridge: only update necessary problems when judge sends update signal
This commit is contained in:
parent
4ee2e1b940
commit
04f9fe8252
3 changed files with 72 additions and 21 deletions
|
@ -25,6 +25,7 @@ from judge.models import (
|
||||||
SubmissionTestCase,
|
SubmissionTestCase,
|
||||||
)
|
)
|
||||||
from judge.bridge.utils import VanishedSubmission
|
from judge.bridge.utils import VanishedSubmission
|
||||||
|
from judge.caching import cache_wrapper
|
||||||
|
|
||||||
logger = logging.getLogger("judge.bridge")
|
logger = logging.getLogger("judge.bridge")
|
||||||
json_log = logging.getLogger("judge.json.bridge")
|
json_log = logging.getLogger("judge.json.bridge")
|
||||||
|
@ -144,16 +145,41 @@ class JudgeHandler(ZlibPacketHandler):
|
||||||
self.problems = set(p for p, _ in problem_packet)
|
self.problems = set(p for p, _ in problem_packet)
|
||||||
|
|
||||||
def _update_judge_problems(self):
|
def _update_judge_problems(self):
|
||||||
problem_codes = list(self.problems)
|
|
||||||
chunk_size = 500
|
chunk_size = 500
|
||||||
self.judge.problems.clear()
|
|
||||||
|
|
||||||
for i in range(0, len(problem_codes), chunk_size):
|
target_problem_codes = self.problems
|
||||||
chunk = problem_codes[i : i + chunk_size]
|
current_problems = _get_judge_problems(self.judge)
|
||||||
problem_ids = Problem.objects.filter(code__in=chunk).values_list(
|
|
||||||
"id", flat=True
|
updated = False
|
||||||
)
|
problems_to_add = list(target_problem_codes - current_problems)
|
||||||
self.judge.problems.add(*problem_ids)
|
problems_to_remove = list(current_problems - target_problem_codes)
|
||||||
|
|
||||||
|
if problems_to_add:
|
||||||
|
for i in range(0, len(problems_to_add), chunk_size):
|
||||||
|
chunk = problems_to_add[i : i + chunk_size]
|
||||||
|
problem_ids = Problem.objects.filter(code__in=chunk).values_list(
|
||||||
|
"id", flat=True
|
||||||
|
)
|
||||||
|
if not problem_ids:
|
||||||
|
continue
|
||||||
|
logger.info("%s: Add %d problems", self.name, len(problem_ids))
|
||||||
|
self.judge.problems.add(*problem_ids)
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
if problems_to_remove:
|
||||||
|
for i in range(0, len(problems_to_remove), chunk_size):
|
||||||
|
chunk = problems_to_remove[i : i + chunk_size]
|
||||||
|
problem_ids = Problem.objects.filter(code__in=chunk).values_list(
|
||||||
|
"id", flat=True
|
||||||
|
)
|
||||||
|
if not problem_ids:
|
||||||
|
continue
|
||||||
|
logger.info("%s: Remove %d problems", self.name, len(problem_ids))
|
||||||
|
self.judge.problems.remove(*problem_ids)
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
if updated:
|
||||||
|
_get_judge_problems.dirty(self.judge)
|
||||||
|
|
||||||
def _connected(self):
|
def _connected(self):
|
||||||
judge = self.judge = Judge.objects.get(name=self.name)
|
judge = self.judge = Judge.objects.get(name=self.name)
|
||||||
|
@ -194,6 +220,8 @@ class JudgeHandler(ZlibPacketHandler):
|
||||||
def _disconnected(self):
|
def _disconnected(self):
|
||||||
Judge.objects.filter(id=self.judge.id).update(online=False)
|
Judge.objects.filter(id=self.judge.id).update(online=False)
|
||||||
RuntimeVersion.objects.filter(judge=self.judge).delete()
|
RuntimeVersion.objects.filter(judge=self.judge).delete()
|
||||||
|
self.judge.problems.clear()
|
||||||
|
_get_judge_problems.dirty(self.judge)
|
||||||
|
|
||||||
def _update_ping(self):
|
def _update_ping(self):
|
||||||
try:
|
try:
|
||||||
|
@ -931,3 +959,8 @@ class JudgeHandler(ZlibPacketHandler):
|
||||||
|
|
||||||
def on_cleanup(self):
|
def on_cleanup(self):
|
||||||
db.connection.close()
|
db.connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
@cache_wrapper(prefix="gjp", timeout=3600)
|
||||||
|
def _get_judge_problems(judge):
|
||||||
|
return set(judge.problems.values_list("code", flat=True))
|
||||||
|
|
|
@ -561,23 +561,36 @@ class Problem(models.Model, PageVotable, Bookmarkable):
|
||||||
cache.set(key, result)
|
cache.set(key, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def handle_code_change(self):
|
||||||
|
has_data = hasattr(self, "data_files")
|
||||||
|
has_pdf = bool(self.pdf_description)
|
||||||
|
if not has_data and not has_pdf:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
problem_data_storage.rename(self.__original_code, self.code)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if has_pdf:
|
||||||
|
self.pdf_description.name = problem_directory_file_helper(
|
||||||
|
self.code, self.pdf_description.name
|
||||||
|
)
|
||||||
|
super().save(update_fields=["pdf_description"])
|
||||||
|
|
||||||
|
if has_data:
|
||||||
|
self.data_files._update_code(self.__original_code, self.code)
|
||||||
|
|
||||||
def save(self, should_move_data=True, *args, **kwargs):
|
def save(self, should_move_data=True, *args, **kwargs):
|
||||||
code_changed = self.__original_code and self.code != self.__original_code
|
code_changed = self.__original_code and self.code != self.__original_code
|
||||||
super(Problem, self).save(*args, **kwargs)
|
super(Problem, self).save(*args, **kwargs)
|
||||||
if code_changed and should_move_data:
|
if code_changed and should_move_data:
|
||||||
if hasattr(self, "data_files") or self.pdf_description:
|
self.handle_code_change()
|
||||||
try:
|
|
||||||
problem_data_storage.rename(self.__original_code, self.code)
|
def delete(self, *args, **kwargs):
|
||||||
except OSError as e:
|
super().delete(*args, **kwargs)
|
||||||
if e.errno != errno.ENOENT:
|
problem_data_storage.delete_directory(self.code)
|
||||||
raise
|
|
||||||
if self.pdf_description:
|
|
||||||
self.pdf_description.name = problem_directory_file_helper(
|
|
||||||
self.code, self.pdf_description.name
|
|
||||||
)
|
|
||||||
super().save(update_fields=["pdf_description"])
|
|
||||||
if hasattr(self, "data_files"):
|
|
||||||
self.data_files._update_code(self.__original_code, self.code)
|
|
||||||
|
|
||||||
save.alters_data = True
|
save.alters_data = True
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import yaml
|
import yaml
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import shutil
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
@ -48,6 +49,10 @@ class ProblemDataStorage(FileSystemStorage):
|
||||||
def rename(self, old, new):
|
def rename(self, old, new):
|
||||||
return os.rename(self.path(old), self.path(new))
|
return os.rename(self.path(old), self.path(new))
|
||||||
|
|
||||||
|
def delete_directory(self, name):
|
||||||
|
directory_path = self.path(name)
|
||||||
|
shutil.rmtree(directory_path)
|
||||||
|
|
||||||
|
|
||||||
class ProblemDataError(Exception):
|
class ProblemDataError(Exception):
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
|
|
Loading…
Reference in a new issue