尝试修复 submission 中的竞争问题

This commit is contained in:
virusdefender
2019-03-26 09:21:00 +08:00
parent b963f2d5bc
commit 7e4132bed1
3 changed files with 46 additions and 19 deletions

View File

@@ -0,0 +1,23 @@
# Generated by Django 2.1.7 on 2019-03-26 02:01
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contest', '0009_auto_20180501_0436'),
]
operations = [
migrations.AlterUniqueTogether(
name='acmcontestrank',
unique_together={('user', 'contest')},
),
migrations.AlterUniqueTogether(
name='oicontestrank',
unique_together={('user', 'contest')},
),
]

View File

@@ -74,6 +74,7 @@ class ACMContestRank(AbstractContestRank):
class Meta: class Meta:
db_table = "acm_contest_rank" db_table = "acm_contest_rank"
unique_together = (("user", "contest"),)
class OIContestRank(AbstractContestRank): class OIContestRank(AbstractContestRank):
@@ -84,6 +85,7 @@ class OIContestRank(AbstractContestRank):
class Meta: class Meta:
db_table = "oi_contest_rank" db_table = "oi_contest_rank"
unique_together = (("user", "contest"),)
class ContestAnnouncement(models.Model): class ContestAnnouncement(models.Model):

View File

@@ -187,8 +187,9 @@ class JudgeDispatcher(DispatcherBase):
logger.info( logger.info(
"Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id) "Contest debug mode, id: " + str(self.contest_id) + ", submission id: " + self.submission.id)
return return
self.update_contest_problem_status() with transaction.atomic():
self.update_contest_rank() self.update_contest_problem_status()
self.update_contest_rank()
else: else:
if self.last_result: if self.last_result:
self.update_problem_status_rejudge() self.update_problem_status_rejudge()
@@ -329,29 +330,30 @@ class JudgeDispatcher(DispatcherBase):
if self.contest.rule_type == ContestRuleType.OI or self.contest.real_time_rank: if self.contest.rule_type == ContestRuleType.OI or self.contest.real_time_rank:
cache.delete(f"{CacheKey.contest_rank_cache}:{self.contest.id}") cache.delete(f"{CacheKey.contest_rank_cache}:{self.contest.id}")
with transaction.atomic(): def get_rank(model):
if self.contest.rule_type == ContestRuleType.ACM: return model.objects.select_for_update().get(user_id=self.submission.user_id, contest=self.contest)
model = ACMContestRank
func = self._update_acm_contest_rank
else:
model = OIContestRank
func = self._update_oi_contest_rank
if self.contest.rule_type == ContestRuleType.ACM:
model = ACMContestRank
func = self._update_acm_contest_rank
else:
model = OIContestRank
func = self._update_oi_contest_rank
try:
rank = get_rank(model)
except model.DoesNotExist:
try: try:
# todo unique index model.objects.create(user_id=self.submission.user_id, contest=self.contest)
# func 也不是安全的 rank = get_rank(model)
rank = model.objects.get(user_id=self.submission.user_id, contest=self.contest) except IntegrityError:
except ACMContestRank.DoesNotExist: rank = get_rank(model)
try: func(rank)
rank = model.objects.create(user_id=self.submission.user_id, contest=self.contest)
except IntegrityError:
rank = model.objects.get(user_id=self.submission.user_id, contest=self.contest)
func(rank)
def _update_acm_contest_rank(self, rank): def _update_acm_contest_rank(self, rank):
info = rank.submission_info.get(str(self.submission.problem_id)) info = rank.submission_info.get(str(self.submission.problem_id))
# 因前面更改过,这里需要重新获取 # 因前面更改过,这里需要重新获取
problem = Problem.objects.get(contest_id=self.contest_id, id=self.problem.id) problem = Problem.objects.select_for_update().get(contest_id=self.contest_id, id=self.problem.id)
# 此题提交过 # 此题提交过
if info: if info:
if info["is_ac"]: if info["is_ac"]: