2013-03-11 5 views
0

В проекте, над которым я сейчас работаю, мы имеем то, что мы называем проблемами. Задачи имеют членов и участников. Участниками являются все, у кого есть доступ к задаче (которая может быть как отдельными пользователями, так и группами пользователей), а участники отслеживают статистику об участии на основе каждого пользователя.Обработка уникальности одновременно в системах на основе событий

Участники вызова пересчитываются каждый раз, когда добавляется новый участник задачи. Это происходит на основе событий, так что участник вызова запускает событие created, на которое участник опроса слушает.

Проблема возникает, когда два объекта вызова создаются одновременно, то есть событие также запускается дважды, а два исполнения кода выполняются одновременно. Для иллюстрации:

challenge.getMembers(); 
challenge.getParticipants(); 

// calculations 

foreach member not in participants, create participant 

Как уже говорилось, проблема возникает тогда, когда выше код работает одновременно, более конкретно, когда второй казни достигает getParticipants перед первым создавший участника записей в там отсутствовали. Оба исполнения показывают, что некоторые участники отсутствуют и создают их. Это означает, что теперь у нас есть дубликаты записей участников вызова для некоторых пользователей.

Прямо сейчас наше решение этой проблемы - иметь уникальный индекс на challenge_id, user_id в списке участников. Он чувствует себя немного грязным, но просто игнорирует ошибку при нарушении ограничения. Это также затрудняет проверку других ошибок SQL, поскольку все ошибки просто передаются обратному вызову, и тогда нам нужно будет проверить содержимое строки ошибки, чтобы увидеть, была ли она ошибкой (нарушение уникальности) или ошибки, которые нам не нравятся (например, плохой синтаксис), которые должны обрабатываться.

Этот код будет работать на нескольких серверах, поэтому даже с помощью мутаций мы не гарантируем, что код не будет запускаться одновременно. Мы также не против, чтобы он выполнялся одновременно для различных задач, если он не запускается одновременно для одной и той же задачи. Есть ли у кого-нибудь предложения по обработке параллельных слияний этого типа?

+3

Простое решение (хотя, вероятно, и не слишком эффективное) было бы поддерживать блокировку за вызов в вашей базе данных или какой-то кэш/разделяемый уровень памяти, доступный для всех ваших серверов. Однако большая проблема заключается в вашем дизайне; почему вы создаете всех участников при добавлении нового участника? Почему вы не просто создаете каждого участника в качестве участника, когда этот член добавлен? Кроме того, у вас должно быть это уникальное ограничение на ваш дБ, просто если вы считаете, что случай не должен происходить. Это хороший резерв, если ваш код не так хорош, как вы думаете. –

+0

Как выглядит ваша схема для задач, участников и участников? – arnorhs

ответ

0

Поскольку вы говорите, что не против одновременного запуска кода для различных задач, у вас может быть столбец с состоянием в таблице вызовов. Затем вы выберете UPDATE challenges SET state = {locked} WHERE id = {id} AND state = {unlocked}. Если этот запрос завершается успешно, а затронутые строки равны 1, вы можете продолжить обновление участников и разблокировать вызов после этого. Если запрос не влияет на какие-либо строки, тогда проблема, вероятно, обновляется каким-то другим потоком выполнения, поэтому вы можете просто пропустить этот шаг или запланировать его повторное выполнение позже.

Если вы идете по этому маршруту, вам нужно быть осторожным, чтобы разблокировать свои проблемы и сделать что-то, что мешает им застревать, если один из потоков падает в середине обновления и не разблокирует его вызов. Например, у вас может быть state = {thread id}, а затем вы можете определить, какие потоки мертвы, и разблокировать их проблемы.

 Смежные вопросы

  • Нет связанных вопросов^_^