pg_send_query (): не удается установить соединение в режим блокировки? - PullRequest
12 голосов
/ 14 мая 2009

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

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

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

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

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

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

Ответы [ 6 ]

24 голосов
/ 14 октября 2009

Это признак того, что pg_send_query() не может успешно переключить соединение обратно в режим блокировки. Глядя на исходный код в PHPs 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, или блокировка, если аргумент равен 0. Возвращает 0, если ОК, -1, если ошибка.

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

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

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

/* PQsetnonblocking:
 * sets the PGconn's 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 не завершился успешно, что указывает на наличие остатка в буфере вывода соединения.

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

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

3 голосов
/ 14 июня 2013

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

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

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

3 голосов
/ 14 октября 2009

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

Пример, приведенный в документах для 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()? Если вы можете позволить вашему сценарию блокировать ожидание выполнения запроса, я предполагаю (правда, не пытаясь это сделать), что вы не увидите эти ошибки.

2 голосов
/ 31 марта 2017

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

2 голосов
/ 24 августа 2015

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

Это происходит, когда постоянное соединение с pg_pconnect () потеряно и pgsql.auto_reset_persistent установлено на Off .

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

  1. PHP сессия истекает
  2. Время ожидания подключения к БД
  3. Сервер веб-сервера / БД перезапущен

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

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

2 голосов
/ 25 сентября 2012

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

pg_connect("...", PGSQL_CONNECT_FORCE_NEW)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...