Клиенты Redis могут одновременно обновлять кеш, что приводит к сохранению неправильного состояния

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

Моя проблема заключается в том, что когда пользователь выполняет задачу, его оценка обновляется, что означает, что он обновит запись в Redis, заменив предыдущее значение новым (в моем случае он заменит весь объект комнаты новым, даже если комната не изменилась, а изменился только счет игрока внутри комнаты).

Дело в том, что если несколько пользователей выполнят задачу одновременно, они будут одновременно отправлять новую запись в redis, и только последний получит обновление.

Например:

В кеше Redis это начальное значение: { roomId: "...", score:[{ "player1": 0 }, { "player2": 0 }] }

Игрок 1 выполняет задание и отправляет:

{ roomId: "...", score:[{ "player1": 1 }, { "player2": 0 }] }

При этом Игрок 2 выполняет задание и отправляет:

{ roomId: "...", score:[{ "player1": 0 }, { "player2": 1 }] }

В кеше Redis сначала будет сохранено значение, полученное, скажем, от игрока 1, а затем значение от игрока 2, что означает, что новое значение в кеше будет:

{ roomId: "...", score:[{ "player1": 0 }, { "player2": 1 }] }

Хотя это неправильно, потому что правильное значение будет: { roomId: "...", score:[{ "player1": 1 }, { "player2": 1 }] } где присутствуют оба изменения.

На данный момент я также использую систему pub/sub для отслеживания изменений, чтобы они отражались на каждом сервере и каждом пользователе, подключенном к серверу.

Что я могу сделать, чтобы исправить это? Для справки рассмотрим следующее изображение как архитектуру системы:

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема заключается в том, что вы чередуете один набор операций чтения/записи с другими, что приводит к использованию устаревших данных при обновлении ключей. К счастью, это (относительно) легко исправить: просто объедините блок операций чтения/записи в единую атомарную единицу, используя Lua-скрипт , транзакцию или, что еще проще, с помощью одного RedisJSON. команда.

Вот пример использования RedisJSON. Сначала подготовьте ключ/документ JSON, в котором будут храниться все оценки для комнаты, используя команду JSON.SET:

> JSON.SET room:foo $ '{ "roomId": "foo", "score": [] }'
OK

После этого используйте команду JSON.ARRAPPEND, если вам нужно добавить элемент в массив оценок:

> JSON.ARRAPPEND room:foo $.score '{ "player1": 123 }'
1
...
> JSON.ARRAPPEND room:foo $.score '{ "player2": 456 }'
2

Получить весь документ JSON так же просто, как запустить:

> JSON.GET room:foo
"{\"roomId\":\"foo\",\"score\":[{\"player1\":123},{\"player2\":456}]}"

Другие вопросы по теме