1

我正在谷歌应用引擎上制作成绩册。我会在每个评分期间跟踪每个学生的成绩。评分周期可以重叠。因为我可能一次显示数百个这样的成绩,所以我预先计算了服务器上的成绩。所以,对于任何一个学生,我可能有很多计算好的成绩——每个评分期都有一个。

现在,老师从测验中输入一个新分数。该分数可能会影响许多计算的成绩,因为它可能属于许多评分期。我需要重新计算所有受影响的成绩。这可能需要很长时间,因为对于每个评分期,我需要获取所有相关分数并对这些分数执行复杂的例程。我认为 30 秒是不够的 - 特别是如果今天数据存储区感觉很慢的话。此外,失败不是一种选择。一些成绩更新而另一些成绩悄然过时是不可接受的。

所以我心想,学习任务队列的时间真是太棒了!

我不是数据库结构或任何方面的专家,但这里是我想要做的事情的概述:

public ReturnCode addNewScore(Float score, Date date, Long studentId)
{
    List<CalculatedGrade> existingGrades = getAllRelevantGradesForStudent(studentId, date);

    for (CalculatedGrade grade : existingGrades)
    {
        grade.markDirty(); //leaves a record that this grade is no longer up to date
    }

    persistenceManager.makePersistentAll(existingGrades);
    //DANGER ZONE?
    persistenceManager.makePersistent(new IndividualScore(score, date, studentId));

    tellTheTaskQueueToStartCalculating();

    return OMG_IT_WORKED;
}

这似乎是一种将所有相关等级标记为脏的快速方法。如果中途失败,则返回失败,客户端将知道重试。如果客户端稍后尝试获取脏等级,我们可以在那里返回错误。

然后,任务队列代码将如下所示:

public void calculateThemGrades()
{
    List<CalculatedGrade> dirtyGrades = getAllDirtyGrades();

    try
    {
        for (CalculatedGrade grade : dirtyGrades)
        {
            List<Score> relevantScores = getAllRelevantScores();
            Float cleanGrade = calculateGrade(relevantScores);
            grade.setGrade(cleanGrade);
            grade.markClean();

            persistenceManager.flush();
        }
    }
    catch(Throwable anything)
    {
        //if there was any problem, like we ran out of time or the datastore is down or whatever, just try again
        tellTheTaskQueueToStartCalculating()
    }
}

这是我的问题:这是否保证在添加新分数后永远不会有一个被标记为干净的计算成绩?

具体关注领域:

  • 在危险区域周围的第一个片段中,existingGrades总是会在新的之前持续存在吗?IndividualScore
  • 是否有可能另一个线程将在危险区域启动任务队列代码,以便在IndividualScore真正进入新的之前,这些现有的等级可能会再次被标记为干净?如果是这样,我如何确保不会发生这种情况(所有年级的交易都已结束)?
  • persistenceManager.flush()即使 pm 没有关闭,是否足以保存部分完成的计算?

这一定是一个常见的问题。我将不胜感激任何指向教程的链接,尤其是那些 appengine 的链接。感谢您阅读这么多!

4

1 回答 1

2

如果您担心竞争条件,请不要使用布尔脏标志 - 而是使用一对时间戳。当您想将记录标记为脏时,请更新“脏”时间戳。

当你开始计算成绩时,记下“脏”时间戳是什么。

计算完成绩后,将“干净”时间戳更新为等于您开始时读取的“脏”时间戳的值,这表示您已将该成绩与该时间戳的新数据同步。

任何“脏”时间戳大于其“干净”时间戳的记录都是脏的。两场比赛干净的任何记录。简单有效。如果另一个请求添加了会影响给定成绩的新数据,而您的任务队列任务已经在计算成绩,“脏”时间戳将与更新的“干净”时间戳不匹配,因此任务队列将考虑记录仍然很脏,然后再次处理。

于 2011-02-24T23:08:34.400 回答