У меня есть несколько рабочих, каждый из которых имеет собственную связь с PostgreSQL. Рабочие манипулируют разными таблицами.
Рабочие обрабатывают параллельные запросы извне системы. Одной из таблиц, к которым осуществляется доступ, является таблица пользователей. Когда приходит какая-то информация, я сначала должен убедиться, что в таблице есть запись для пользователя. Если нет записи, я хочу сначала создать ее.
Я использую следующую идиому:
if [user does not exist] then [create user]
Код [user does not exist]
:
SELECT id FROM myschema.users WHERE userId='xyz'
и я проверяю, возвращается ли какая-либо строка.
(упрощенным) кодом [create user]
является:
INSERT INTO myschema.users VALUES ('xyz')
Когда моя система обрабатывает параллельные потоки различной информации о одном и том же пользователе, я часто получаю ошибку PostgreSQL:
Key (id)=(xyz) already exists
Это происходит потому, что команда SELECT
не возвращает никаких строк, затем другой рабочий создает пользователя, любой мой рабочий пытается сделать то же самое, что приводит к примерной ошибке concurrency.
Согласно документации PostgreSQL по умолчанию, когда я неявно запускаю транзакцию, таблица становится заблокированной до тех пор, пока я ее не фиксирую. Я не использую autocommit, и я совершаю транзакцию только в блоках, например. после всего блока if-else
.
В самом деле, я мог бы поместить материал if-else
в SQL напрямую, но он не решает мою проблему блокировки вообще. Я предполагал, что парадигма "победит все это" будет работать, и что первый рабочий, которому удается выполнить команду SELECT
, будет иметь блокировки, пока не назовет COMMIT
.
Я прочитал много разных тем здесь, в SO, но я все еще не уверен, что такое правильное решение. Должен ли я использовать явное блокирование таблиц, потому что неявная блокировка не работает? Как я могу гарантировать, что только один рабочий владеет таблицей во время?