Согласованная база данных с одновременным чтением и записью - PullRequest
0 голосов
/ 28 марта 2019

Я написал REST API для простой игры, включающей создание, присоединение и отправку игровых матчей. Я столкнулся с проблемой при тестировании сервера. Отправляя запросы на присоединение одним игроком (их много одновременно), сервер присоединяет игрока к лобби многократно.

Мой дизайн выглядит так:

  • в таблице игроков проверьте, установлено ли на поле игрока 'playing' значение true, если да, отмените запрос
  • , если игрок в данный момент не играет, создайте новую запись игрока для лобби и установите для статуса игры игрока значение true

Я знаю, что проблема заключается в проверке, играет ли игрок в данный момент. Каждый запрос выполняется отдельной программой в Go, поэтому может случиться так, что каждая программа получает из базы данных информацию о том, что игровое поле игрока равно false. Тогда каждый гурунт будет добавлять в лобби одного и того же игрока, чего я хочу избежать.

Есть ли способ избежать этой проблемы, или проблема в моем дизайне?

Ответы [ 3 ]

0 голосов
/ 28 марта 2019

Ваш вопрос связан не с Go, а со структурой базы данных.

Чтобы упростить решение:

Чтобы убедиться, что одновременно играет только один игрок, вы можете создать для этого отдельную таблицу с уникальным индексом / ограничением.

Как добавить уникальность в 2 столбца, см. Этот вопрос: В Postgresql, Force уникальный для комбинации из двух столбцов В вашем случае столбцы будут player_id и playing

На уровне API вам необходимо обрабатывать ошибки, которые вы получаете из базы данных, если игрок уже играет.

0 голосов
/ 28 марта 2019

Если вы хотите, чтобы у вас никогда не было одного и того же пользователя в одном и том же лобби дважды, вы должны добавить уникальное ограничение на комбинацию этих двух полей в таблице игроков (предполагая, что players - это то, что связывает users с lobbies).

Получив это, вы можете использовать upsert :

INSERT INTO players (user_id, lobby_id, playing)  VALUES (12, 41, 1) ON CONFLICT name_of_your_constraint UPDATE players.playing = 1;

Это создаст запись, но, если это невозможно, поскольку комбинация user и lobby уже существует, она просто помечает пользователя как играющего.

0 голосов
/ 28 марта 2019

Есть несколько вариантов - стоит прочитать документы на Postgres для обработки параллелизма.

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

Итак, что-то вроде:

begin;

insert into lobby
select player_id, ...
from players
where player_id = $current_player
and player_id not in (select player_id from lobby);

update player
set playing = 1
where playing = 0
and player_id = $player_id;

commit;

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

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