Как безопасно убить запрос, время которого истекло - PullRequest
0 голосов
/ 04 апреля 2019

Я использую PostgreSQL JDBC, и у меня есть соединение для некоторых запросов выбора и вставки.Некоторые запросы занимают некоторое время, поэтому я добавил время ожидания.Проблема заключается в том, что тайм-аут закрывает соединение, но запрос все еще выполняется на сервере БД, и он создает блокировки.

Упрощенный код для проблемы (Реальный код намного сложнее и больше, но это не так.не имеет значения):

    PGPoolingDataSource source = new PGPoolingDataSource();
    source.setUrl(url);
    source.setUser(user);
    source.setPassword(password);
    source.setMaxConnections(10);
    source.setSocketTimeout(5); // Timeout of 5 seconds

    // Insert a row data, and make timeout
    Connection con = source.getConnection();
    con.setAutoCommit(false);
    try {
        Statement st2 = con.createStatement();
        st2.execute("insert into data.person values (4, 'a')");

        Statement st3 = con.createStatement();
        st3.executeQuery("select pg_sleep(200)"); // A query which takes a lot of time and causes a timeout
        con.commit();
        con.close();
    } catch (SQLException ex) {

        if (!con.isClosed()) {
            con.rollback();
            con.close();
        }
        ex.printStackTrace();
    }


    Connection con2 = source.getConnection();
    con2.setAutoCommit(false);
    try {
        Statement st2 = con2.createStatement();

        // This insert query is locked because the query before is still executed, and the rollback didn't happen yet, and the row with the id of 4 is not released
        st2.execute("insert into data.person values (4, 'b')");
        con2.commit();
        con2.close();
    } catch (SQLException ex) {

        if (!con2.isClosed()) {
            con2.rollback();
            con2.close();
        }
        ex.printStackTrace();
    }

(data.person - таблица с id и name.)

Тайм-аут закрывает соединение, и оно даже не получаетсяна линию con.rollback();.Я читал, что когда возникает исключение в запросе, он выполняет откат в фоновом режиме, поэтому все в порядке.

Но запрос занимает много времени (несколько часов), и в результате откат будетпроисходит после того, как большой запрос на выбор завершен.Таким образом, я не могу добавить строку в data.person в течение нескольких часов (Во второй раз, когда я пытаюсь вставить, я получаю исключение тайм-аута, потому что он ожидает снятия блокировки ...).

Я прочитал, что могу использовать функцию pg_terminate_backend в PostgreSQL для завершения запроса, и поэтому я могу выполнить запрос вставки во второй раз.

Мои вопросы:

1) КакБезопасно ли это?

2) Насколько распространено это решение?

3) Есть ли более безопасное решение, которое предоставляют JDBC или PostgreSQL?

1 Ответ

1 голос
/ 04 апреля 2019

pg_terminate_backend будет работать и является безопасной и правильной процедурой, если вы хотите прервать запрос и закрыть соединение с базой данных.

Существует также pg_cancel_backend, который прервет запрос, но оставит соединение открытым.

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

Вы должны выполнить эти операторы для соединения с базой данных, отличного от исходного!

Другой, возможно, более простой способ - установить statement_timeout. Это может быть установлено в файле конфигурации или для отдельного сеанса или транзакции. Чтобы установить его для транзакции, используйте:

BEGIN;  -- in JDBC, use setAutoCommit(false)
SET LOCAL statement_timeout = 30000;  -- in milliseconds
SELECT /* your query */;
COMMIT;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...