Найдите недостатки!Надежное выполнение длинной задачи с помощью очереди задач - PullRequest
1 голос
/ 25 февраля 2011

Я делаю сборник оценок на движке приложений Google. Я отслеживаю оценку каждого студента за период оценки. Оценочные периоды могут перекрываться. Поскольку я могу отображать сотни этих оценок одновременно, я предварительно рассчитываю оценки на сервере. Таким образом, для каждого учащегося у меня может быть много рассчитанных оценок - по одной на каждый оценочный период.

Теперь учитель вводит новый результат из теста. Эта оценка может повлиять на многие из рассчитанных оценок, поскольку она может приходиться на многие периоды оценки. Мне нужно пересчитать все пострадавшие оценки. Это может занять много времени, так как для каждого оценочного периода мне нужно получить все соответствующие оценки и выполнить сложную процедуру над этими оценками. Я думаю, 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() для сохранения частично выполненных вычислений, даже если график не закрыт?

Это должно быть типичная проблема. Буду признателен за любые ссылки на учебники, особенно для appengine. Спасибо за прочтение!

1 Ответ

2 голосов
/ 25 февраля 2011

Если вас беспокоят условия гонки, не используйте логический грязный флаг - вместо этого используйте пару временных меток.Если вы хотите пометить запись как грязную, обновите «грязную» временную метку.

Когда вы начнете вычислять оценку, запишите, какой была «грязная» временная метка.

Когда вы закончитевычисляя оценку, обновите «чистую» временную метку, чтобы она равнялась значению «грязной» временной метки, которую вы прочитали в начале, что означает, что вы синхронизировали эту оценку с новыми данными на эту временную отметку.

Любая запись с «грязной» временной меткой, превышающей ее «чистую» временную метку, является грязной.Любая запись, где два матча чистые.Просто и эффективно.Если другой запрос добавляет новые данные, которые будут влиять на данную оценку, в то время как ваша задача очереди задач уже находится в середине вычисления оценки, «грязная» временная метка не будет соответствовать обновленной «чистой» временной метке, и, таким образом, очередь задач будет рассматривать записьвсе еще грязный и обработайте его снова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...