pg_send_query():无法设置阻塞模式的连接?

时间:2009-05-14 18:42:22

标签: php postgresql

我有一个长时间运行的脚本,似乎偶尔会报告以下NOTICE级错误:     pg_send_query():无法设置与阻止模式的连接

之后似乎继续发送查询,但不清楚它是否成功发送了生成错误的查询。

这是什么症状?

编辑:错误发生时postgres日志中没有条目,这表明这只是一个连接错误,而不是postgres方面出错(例如可能不是postgres崩溃并重新启动或其他东西)

编辑:据我所知,当触发此错误时,我的INSERT语句会以某种方式成功。

修改:2013年6月可能已修复此问题:https://bugs.php.net/bug.php?id=65015

6 个答案:

答案 0 :(得分:24)

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调用的全部目的是将连接置于非阻塞模式以允许异步执行并在之后将其恢复为默认阻塞行为。从documentation of PQsetnonblocking :( PQ_SETNONBLOCKING只是为该函数定义的别名):

  

设置的非阻塞状态   连接。

int PQsetnonblocking(PGconn *conn, int arg);
     

如果arg为1,则将连接状态设置为非阻塞   或者如果arg为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没有成功完成,表明连接输出缓冲区中有剩余的东西。

第一种情况是无害的,因为您的脚本肯定会注意到以后调用丢失的连接并对此作出反应(或者更明显地失败)。

这留下了第二种情况,这意味着您在非默认的非阻塞状态下有连接。我不知道这是否会影响以后会重用此连接的调用。如果你想安全地玩,你可以在这种情况下关闭连接并使用新的/其他连接。

答案 1 :(得分:3)

听起来您正在尝试使用pg_send_query()函数向PostgreSQL发送异步查询。此函数的目的是允许您的PHP脚本继续执行其他代码,同时等待PostgreSQL执行您的查询并使结果准备就绪。

pg_send_query() if (!pg_connection_busy($dbconn)) { pg_send_query($dbconn, "select * from authors; select count(*) from authors;"); } 中给出的示例表明,如果PostgreSQL已经在咀嚼另一个查询,则不应发送查询:

pg_send_query()

您使用pg_query()代替{{1}}是否有理由?如果你可以允许你的脚本阻止等待查询执行,我猜测(诚然没有尝试过)你不会看到这些错误。

答案 2 :(得分:3)

我最近遇到了同样的问题,在Henrik Opels的帮助下,答案意识到在将连接设置回阻塞模式之前,PHP实际上并没有等待缓冲区刷新。

'无法设置与阻塞模式的连接'可以通过足够大的查询来填充发送缓冲区(在末尾填充空格就足够了)。对于较小的查询,我认为它依赖于负载,而是间歇性的。

如果您确实需要异步模式,请尝试https://bugs.php.net/bug.php?id=65015

处的补丁

答案 3 :(得分:2)

如果您使用线程并且正在重复使用连接,则可能会发生这种情况。 如果是这种情况,您可以使用PGSQL_CONNECT_FORCE_NEW,如下所示:

pg_connect("...", PGSQL_CONNECT_FORCE_NEW)

这将强制建立一个新的数据库连接资源,但建议:你可能会用完连接客户端,所以要小心使用这个内部线程,所以不要忘记使用pg_close()

答案 4 :(得分:2)

我在PHP 5.6.9中遇到了相同的错误消息

pg_pconnect()的持久连接丢失时,会发生这种情况 pgsql.auto_reset_persistent 设置为 Off

在以下情况下,连接可能会丢失:

  1. PHP会话过期
  2. 连接数据库超时
  3. 重新启动Web服务器/数据库服务器
  4. 您可以在PHP.ini上查看 pgsql.auto_reset_persistent 并将其设置为 On

    启用 pgsql.auto_reset_persistent 后,每次调用 pg_pconnect()时,都会检查连接链接是否仍然有效。这需要一点开销,但是当conncetion丢失时会出现fixies错误消息。

答案 5 :(得分:2)

我也得到了这个错误。我通过重新启动Web服务器(Apache)解决了我的问题。