Я создаю простое приложение, которое использует 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 для отслеживания изменений, чтобы они отражались на каждом сервере и каждом пользователе, подключенном к серверу.
Что я могу сделать, чтобы исправить это? Для справки рассмотрим следующее изображение как архитектуру системы:
Проблема заключается в том, что вы чередуете один набор операций чтения/записи с другими, что приводит к использованию устаревших данных при обновлении ключей. К счастью, это (относительно) легко исправить: просто объедините блок операций чтения/записи в единую атомарную единицу, используя 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}]}"