Postgresql навсегда зависает на сериализуемой транзакции

Я использую libpqxx для подключения к postgresql. И все было хорошо, пока я не запустил запрос serialazable для одной таблицы в одной строке.

Таблица:

CREATE TABLE t1(id integer primary key);

postgres 9.4.4_x64

pqxx::connection c1(conn_str);
pqxx::connection c2(conn_str);

pqxx::transaction<pqxx::isolation_level::serializable> t1(c1);
t1.exec("INSERT INTO t1 (id) VALUES (25)");

pqxx::transaction<pqxx::isolation_level::serializable> t2(c2);
t2.exec("INSERT INTO t1 (id) VALUES (25)"); //hang here

t2.commit();
t1.commit();

моя программа зависла навсегда. зависать в функции PQexec. Зачем? Я думаю, что это должно откатить одну из транзакций? но нет? просто повесить

ОБНОВЛЕНИЕ: тот же результат для чистого libpq:

c1 = PQconnectdb(conninfo);
c2 = PQconnectdb(conninfo);

res1 = PQexec(c1, "BEGIN");
PQclear(res1);


res1 = PQexec(c1, "INSERT INTO t1 (id) VALUES (104)");
PQclear(res1);

res2 = PQexec(c2, "BEGIN");
PQclear(res2);

res2 = PQexec(c2, "INSERT INTO t1 (id) VALUES (104)");
PQclear(res2);

res2 = PQexec(c2, "END");
PQclear(res2);

res1 = PQexec(c1, "END");
PQclear(res1);

postgresql 9.1 - тот же завис

3 ответа

Зависание не имеет ничего общего с serializable уровень изоляции.

я - нет libpqxx эксперт, но ваш пример, кажется, выполняет обе транзакции в одном потоке. Это твоя проблема.

t2.exec("INSERT INTO t1 (id) VALUES (25)");

Вышеприведенное утверждение должно ждать t1 зафиксировать или откатить до завершения, но t1.commit() никогда не получает шанс выполнить. Тупик! Это абсолютно нормально и произойдет независимо от выбранного вами уровня изоляции. Это всего лишь следствие попытки запустить операторы из двух одновременных транзакций в одном и том же потоке выполнения, но это не очень хорошая идея.

Попробуйте запустить обе транзакции в разных потоках, и ваше зависание исчезнет.

Если задействованы только две транзакции, вы должны получить уникальную ошибку нарушения для транзакции t2 - точно так же, как по умолчанию READ COMMITTED уровень изоляции:

ERROR:  duplicate key value violates unique constraint "t1_pkey"
DETAIL:  Key (id)=(25) already exists.

t1 пробовал вставку первым и выигрывает независимо от того, какая транзакция пытается зафиксировать первой. Все последующие транзакции, пытающиеся вставить один и тот же ключ, ждут первого. Опять же, это действительно для READ COMMITTED а также SERIALIZABLE так.

Возможное объяснение состоит в том, что задействована третья транзакция, которая сначала пыталась вставить тот же ключ и все еще открыта. Или несколько таких транзакций, артефакты ваших тестов.

Все транзакции ждут первой, которая пыталась вставить тот же ключ. Если тот совершает, все остальные получают уникальное нарушение. Если он откатывается, следующий в очереди получает свой шанс.

Чтобы проверить посмотрите на pg_stat_activity (будучи подключенным к той же базе данных):

SELECT * FROM pg_stat_activity;

Более конкретно:

SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction';

Затем подтвердите / откатите незанятую транзакцию. Или грубой силой: разорвать эту связь. Подробности:

Форма команды postgres:

"В параллельном программировании тупик - это ситуация, в которой два или более конкурирующих действия ждут завершения другого, и поэтому ни один из них не делает этого". https://en.wikipedia.org/wiki/Deadlock

определение это не тупик в первую очередь. "c1" не ждет завершения "c2" и может с радостью заниматься своими делами с id=104. Тем не менее, ваше приложение никогда не дает c1 следующей команды, потому что она упорно ждет "c2", чтобы выполнить свою вставку - что она не может из-за блокировки, "c1".

Тестирование блокировки может быть выполнено в одном потоке, но тестирование взаимоблокировки подразумевает, что оба соединения должны одновременно пытаться выполнить команду - что одна программа не может выполнить без многопоточности.

Дэвид Дж.

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