Я использую C# с драйвером Npgsql для подключения к postgresql 9.6.9. У меня есть два сервера, которые подключаются к одной базе данных, выполняя следующий код инициализации в цикле, чтобы убедиться, что они конфликтуют друг с другом:
using (var txScope = new TransactionScope())
using (var connection = Connection())
{
connection.EnlistTransaction(Transaction.Current);
using (var cmd = new NpgsqlCommand("SELECT pg_advisory_lock(1024)", connection))
using (cmd.ExecuteReader()) { }
// The following line fails:
using (var cmd = new NpgsqlCommand(@"CREATE OR REPLACE FUNCTION update_column()
RETURNS TRIGGER AS $$ BEGIN RETURN NEW; END; $$ language 'plpgsql'; ", connection))
{
cmd.ExecuteNonQuery();
}
using (var cmd = new NpgsqlCommand("SELECT pg_advisory_unlock(1024)", connection))
using (cmd.ExecuteReader()) { }
txScope.Complete();
}
Консультативная блокировка должна предотвращать выполнение запроса CREATE более одного раза в настоящее время, но он постоянно дает сбой с «Npgsql.PostgresException (0x80004005): XX000: кортеж одновременно обновляется», где создается функция. Если я использую LOCK TABLE вместо рекомендательной блокировки, это сработает. Если я делаю это без транзакций, тоже работает. Однако это кажется хрупким дизайном, чтобы удалять транзакции каждый раз, когда я хочу заблокировать.
Примечание. Я пробовал это с API транзакций Npgsql и с BEGIN / COMMIT с тем же результатом.
Я думаю, что блокировка правильно работает как мьютекс, так что же не так с транзакцией?
@a_horse_with_no_name Во втором предложении этих документов говорится, что он не поддерживается. Автор имел в виду описать только свою библиотеку, а не весь PostgreSQL ?: npgsql.org/doc/transactions.html
Ну верно предложение «не поддерживает вложенные транзакции». Но «не поддерживает одновременные транзакции» верно только в отношении одновременных транзакций, запущенных в сеансе тем же (= соединение). Несколько сеансов (= соединения) могут абсолютно использовать одновременные транзакции с использованием BEGIN ... COMMIT.
@a_horse_with_no_name Знаете ли вы, поддерживается ли begin / commit / begin / commit в рамках одного соединения? Поскольку соединения объединены в пул, это будет необходимая функция. (У меня были проблемы с BeginTransaction(), но я не помню тестовый пример.)
Я не знаю C# или Npgsql, но Postgres может с этим справиться (он работает без проблем через JDBC)
@a_horse_with_no_name Спасибо за информацию. Метод BeginTransaction определенно не работал с пулом соединений, но я еще не пробовал BEGIN / COMMIT.
Я никогда не использовал npgsql или ado.net. Если бы пришлось, я бы решил эту проблему, установив log_statement=all на сервере, а затем заглянув в файл журнала сервера, чтобы увидеть, что на самом деле было отправлено.
Первый и последний cmd.ExecuteNonQuery (), возможно, не подходят, поскольку содержимое cmd определенно является запросами.
@jjanes Спасибо. Замена их на using (cmd.ExecuteReader()) сработала, но не решила проблему.
@a_horse_with_no_name Ради правильности: pg_advisory_lock () создает блокировку уровня сеанса, на которую транзакции никак не влияют. Блокировку уровня транзакции можно получить с помощью pg_advisory_xact_lock ().





BEGIN ... COMMITполностью поддерживает «параллелизм» - в противном случае никакие транзакции не работали бы в Postgres.