Pg_send_query(): Не удается установить соединение с режимом блокировки?

У меня есть длинный script, который, как представляется, изредка сообщает о следующей ошибке уровня NOTICE:   pg_send_query(): Не удается установить соединение с режимом блокировки

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

Что это за симптом?

Изменить: В журнале postgres нет записей в момент возникновения ошибки, предполагая, что это только ошибка соединения, а не что-то не так на стороне postgres (например, вероятно, не результат postgres сбой и перезапуск или что-то в этом роде)

Изменить:. Насколько я могу судить, мои инструкции INSERT преуспевают, так или иначе, когда эта ошибка срабатывает.

Изменить: Похоже, это могло быть исправлено в июне 2013 года: https://bugs.php.net/bug.php?id=65015

Ответ 1

Это признак pg_send_query() неспособности успешно переключить соединение обратно в режим блокировки. Посмотрев исходный код на PHP pgsql.c, вы можете найти:

/* {{{ proto bool pg_send_query(resource connection, string query)
   Send asynchronous query */
PHP_FUNCTION(pg_send_query)
{

<... snipped function setup stuff ...>

 if (PQ_SETNONBLOCKING(pgsql, 1)) {
  php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
  RETURN_FALSE;
 }

<... snipped main function execution stuff ...>

 if (PQ_SETNONBLOCKING(pgsql, 0)) {
  php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
 }
 RETURN_TRUE;
}

Таким образом, ошибка возникает в конце функции после завершения основной работы. Это соответствует вашему наблюдению, что ваши инструкции INSERT выполняются.

Вся цель двух вызовов PQ_SETNONBLOCKING заключается в том, чтобы поместить соединение в неблокирующий режим, чтобы разрешить асинхронное выполнение и вернуть его обратно в режим блокировки по умолчанию. Из документации по PQsetnonblocking: (PQ_SETNONBLOCKING - это просто псевдоним, определенный для этой функции):

Устанавливает неблокирующий статус соединение.

int PQsetnonblocking(PGconn *conn, int arg);

Устанавливает состояние соединения для неблокирования, если arg равно 1, или блокирование, если arg равно 0. Возвращает 0, если OK, -1, если ошибка.

В неблокирующем состоянии вызовы PQsendQuery, PQputline, PQputnbytes, и PQendcopy не будет блокировать, но вместо этого возвратите ошибку, если они понадобятся для повторного вызова.

Обратите внимание, что PQexec не чтит неблокирующий режим; если он вызывается, это в любом случае будут действовать в блокирующей форме.

Если вы посмотрите на источник PQsetnonblocking (в PostgeSQLs fe-exec.c), есть две возможные причины отказа вызова:

/* PQsetnonblocking:
 * sets the PGconn database connection non-blocking if the arg is TRUE
 * or makes it non-blocking if the arg is FALSE, this will not protect
 * you from PQexec(), you'll only be safe when using the non-blocking API.
 * Needs to be called only on a connected database connection.
 */
int
PQsetnonblocking(PGconn *conn, int arg)
{
 bool  barg;

 if (!conn || conn->status == CONNECTION_BAD)
  return -1;

 barg = (arg ? TRUE : FALSE);

 /* early out if the socket is already in the state requested */
 if (barg == conn->nonblocking)
  return 0;

 /*
  * to guarantee constancy for flushing/query/result-polling behavior we
  * need to flush the send queue at this point in order to guarantee proper
  * behavior. this is ok because either they are making a transition _from_
  * or _to_ blocking mode, either way we can block them.
  */
 /* if we are going from blocking to non-blocking flush here */
 if (pqFlush(conn))
  return -1;

 conn->nonblocking = barg;

 return 0;
}

Таким образом, либо соединение каким-то образом потерялось, либо pqFlush не завершилось успешно, указав остаточный материал в выходном буфере соединения.

Первый случай был бы безвредным, так как ваш script, безусловно, заметит потерянное соединение для последующих вызовов и отреагирует на это (или не станет более заметным).

Это оставляет второй случай, что означает, что у вас есть соединение в состоянии, отличном от неблокируемого. Я не знаю, повлияло ли это на последующие вызовы, которые будут повторно использовать это соединение. Если вы хотите безопасно играть, вы должны закрыть соединение в этом случае и использовать новый/другой.

Ответ 2

Похоже, вы пытаетесь использовать функцию pg_send_query() для отправки асинхронных запросов в PostgreSQL. Цель этой функции - позволить вашему PHP скрипт продолжить выполнение другого кода, ожидая, когда PostgreSQL выполнит ваш запрос и сделает результат готовым.

Пример, приведенный в docs для pg_send_query(), предполагает, что вы не должны отправлять запрос, если PostgreSQL уже пережевывает другой запрос

if (!pg_connection_busy($dbconn)) {
  pg_send_query($dbconn, "select * from authors; select count(*) from authors;");
}

Есть ли причина, по которой вы используете pg_send_query() вместо pg_query()? Если вы можете позволить вашему script блокировать ожидание выполнения запроса, я предполагаю (по общему признанию, не пробовав его), что вы не увидите этих ошибок.

Ответ 3

У меня недавно была та же проблема, и с помощью решения Henrik Opels поняли, что PHP на самом деле не ждет, пока буфер сбросится, прежде чем устанавливать соединение обратно в режим блокировки.

"Невозможно установить соединение с режимом блокировки" тривиально повторяется с достаточно большими запросами для заполнения буфера отправки (достаточно заполнить пробелы в конце). С меньшими запросами я полагаю, что он зависит от нагрузки и довольно прерывистый.

если вам действительно нужен асинхронный режим, попробуйте патч на https://bugs.php.net/bug.php?id=65015

Ответ 4

Это может произойти, если вы используете threads и соединение повторно используется. Если это так, вы можете использовать PGSQL_CONNECT_FORCE_NEW следующим образом:

pg_connect("...", PGSQL_CONNECT_FORCE_NEW)

Это заставит новый ресурс подключения к базе данных, но будет рекомендовано: у вас могут закончиться клиенты клиентов, поэтому будьте осторожны с использованием этих внутренних потоков, поэтому не забудьте использовать pg_close().

Ответ 5

Я тоже получил эту ошибку. Я решаю свою проблему, перезапустив веб-сервер (Apache).

Ответ 6

Я столкнулся с тем же сообщением об ошибке с PHP 5.6.9

Это происходит, когда постоянное соединение, выполняемое pg_pconnect(), теряется и Для параметра pgsql.auto_reset_persistent установлено значение Выкл.

Соединение может потеряться, если:

  • Срок действия сеанса PHP
  • Подключение к тайм-аутам БД
  • Сервер Webserver/DB перезагружен

Вы можете проверить PHP.ini для pgsql.auto_reset_persistent и установить его в On.

Когда pgsql.auto_reset_persistent включен, каждый раз, когда вызывается pg_pconnect(), ссылка соединения проверяется, если она все еще действительна. Это требует небольшого количества накладных расходов, но исправляет сообщение об ошибке при потере связи.