В проекте, над которым я сейчас работаю, мы имеем то, что мы называем проблемами. Задачи имеют членов и участников. Участниками являются все, у кого есть доступ к задаче (которая может быть как отдельными пользователями, так и группами пользователей), а участники отслеживают статистику об участии на основе каждого пользователя.Обработка уникальности одновременно в системах на основе событий
Участники вызова пересчитываются каждый раз, когда добавляется новый участник задачи. Это происходит на основе событий, так что участник вызова запускает событие created
, на которое участник опроса слушает.
Проблема возникает, когда два объекта вызова создаются одновременно, то есть событие также запускается дважды, а два исполнения кода выполняются одновременно. Для иллюстрации:
challenge.getMembers();
challenge.getParticipants();
// calculations
foreach member not in participants, create participant
Как уже говорилось, проблема возникает тогда, когда выше код работает одновременно, более конкретно, когда второй казни достигает getParticipants
перед первым создавший участника записей в там отсутствовали. Оба исполнения показывают, что некоторые участники отсутствуют и создают их. Это означает, что теперь у нас есть дубликаты записей участников вызова для некоторых пользователей.
Прямо сейчас наше решение этой проблемы - иметь уникальный индекс на challenge_id, user_id
в списке участников. Он чувствует себя немного грязным, но просто игнорирует ошибку при нарушении ограничения. Это также затрудняет проверку других ошибок SQL, поскольку все ошибки просто передаются обратному вызову, и тогда нам нужно будет проверить содержимое строки ошибки, чтобы увидеть, была ли она ошибкой (нарушение уникальности) или ошибки, которые нам не нравятся (например, плохой синтаксис), которые должны обрабатываться.
Этот код будет работать на нескольких серверах, поэтому даже с помощью мутаций мы не гарантируем, что код не будет запускаться одновременно. Мы также не против, чтобы он выполнялся одновременно для различных задач, если он не запускается одновременно для одной и той же задачи. Есть ли у кого-нибудь предложения по обработке параллельных слияний этого типа?
Простое решение (хотя, вероятно, и не слишком эффективное) было бы поддерживать блокировку за вызов в вашей базе данных или какой-то кэш/разделяемый уровень памяти, доступный для всех ваших серверов. Однако большая проблема заключается в вашем дизайне; почему вы создаете всех участников при добавлении нового участника? Почему вы не просто создаете каждого участника в качестве участника, когда этот член добавлен? Кроме того, у вас должно быть это уникальное ограничение на ваш дБ, просто если вы считаете, что случай не должен происходить. Это хороший резерв, если ваш код не так хорош, как вы думаете. –
Как выглядит ваша схема для задач, участников и участников? – arnorhs