Подтвердить что ты не робот

Postgres - ОШИБКА: подготовленная инструкция "S_1" уже существует

При выполнении пакетных запросов через JDBC для pgbouncer я получаю следующую ошибку:

org.postgresql.util.PSQLException: ERROR: prepared statement "S_1" already exists

Я нашел отчеты об ошибках в Интернете, но все они, похоже, имеют дело с Postgres 8.3 или ниже, тогда как мы работаем с Postgres 9.

Здесь код, который вызывает ошибку:

this.getJdbcTemplate().update("delete from xx where username = ?", username);

this.getJdbcTemplate().batchUpdate( "INSERT INTO xx(a, b, c, d, e) " + 
                "VALUES (?, ?, ?, ?, ?)", new BatchPreparedStatementSetter() {
    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        ps.setString(1, value1);
        ps.setString(2, value2);
        ps.setString(3, value3);
        ps.setString(4, value4);
        ps.setBoolean(5, value5);
    }
    @Override
    public int getBatchSize() {
        return something();
    }
});

Кто-нибудь видел это раньше?

Изменить 1:

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

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

Изменить 2:

Я наткнулся на эту ссылку, где плакат перевернул собственный патч. В настоящее время мы изучаем его применение для наших собственных целей, если оно окажется безопасным и эффективным.

4b9b3361

Ответ 1

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

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

Ответ 2

Новый, лучший ответ

Чтобы отменить состояние сеанса и эффективно забыть подготовленный оператор "S_1" , используйте параметр server_reset_query в конфигурации PgBouncer.

Старый ответ

См. http://pgbouncer.projects.postgresql.org/doc/faq.html#_how_to_use_prepared_statements_with_transaction_pooling

Переход в режим сеанса не является идеальным решением. Трансакционный пул намного эффективнее. Но для объединения транзакций вам нужны вызовы DB без учета состояния.

Я думаю, у вас есть три варианта:

  • Отключить PS в драйвере jdbc,
  • вручную освободить их в коде Java,
  • настройте pgbouncer, чтобы отменить их при завершении транзакции.

Я бы попробовал вариант 1 или вариант 3 - в зависимости от того, каким образом ваше приложение использует их.

Для получения дополнительной информации прочитайте документы:

http://pgbouncer.projects.postgresql.org/doc/config.html (поиск для server_reset_query),

или google для этого:

postgresql jdbc +preparethreshold

Ответ 3

Отключение готовых операторов в JDBC. Правильный способ сделать это для JDBC - добавить параметр "prepareThreshold = 0" для подключения строки.

jdbc:postgresql://ip:port/db_name?useAffectedRows=true&prepareThreshold=0