代码之家  ›  专栏  ›  技术社区  ›  Frank Farmer

pg_send_query():无法将连接设置为阻止模式?

  •  12
  • Frank Farmer  · 技术社区  · 16 年前

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

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

    这是什么症状?

    编辑: 发生错误时,postgres日志中没有条目,这表明这只是一个连接错误,而不是postgres一侧发生的错误(例如,可能不是postgres崩溃和重新启动的结果或其他原因)。

    编辑: 据我所知,当这个错误被触发时,我的insert语句以某种方式是成功的。

    编辑: 看起来这可能已经在2013年6月修复了: https://bugs.php.net/bug.php?id=65015

    6 回复  |  直到 8 年前
        1
  •  24
  •   Henrik Opel    16 年前

    它是一种症状 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 调用是将连接置于非阻塞模式,以允许异步执行,然后将其恢复为默认阻塞行为。从 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没有成功完成,这表示连接输出缓冲区中的剩余内容。

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

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

        2
  •  3
  •   Bill Karwin    16 年前

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

    中给出的示例 docs 对于 PGy-SeNdSyQuy() 如果PostgreSQL已经在咀嚼另一个查询,建议您不要发送查询:

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

    你为什么要用 PGy-SeNdSyQuy() 而不是 pg_query() ?如果您可以允许您的脚本阻止等待查询执行,我猜(必须承认,没有尝试过)您不会看到这些错误。

        3
  •  3
  •   Vektah    12 年前

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

    “cannot set connection to blocking mode”可重复使用足够大的查询来填充发送缓冲区(在末尾填充空格就足够了)。对于较小的查询,我认为它依赖于负载,而且是间歇性的。

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

        4
  •  2
  •   J. Costa    13 年前

    如果使用 螺纹 连接正在被重用。 如果是这种情况,你可以使用 PGSQL_CONNECT_FORCE_NEW 这样地:

    pg_connect("...", PGSQL_CONNECT_FORCE_NEW)
    

    这将强制新的数据库连接资源,但建议: 您可能会耗尽连接客户端 ,所以小心地使用这个内螺纹,不要忘记使用 pg_close() .

        5
  •  2
  •   Zenithies    10 年前

    我在php 5.6.9中遇到了同样的错误消息

    当持续连接由 PGYP-连接() 迷路了 pgsql.auto重置持久性 设置为 下车 .

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

    1. PHP会话过期
    2. 连接到数据库超时
    3. Web服务器/DB服务器已重新启动

    您可以检查php.ini pgsql.auto重置持久性 并将其设置为 .

    pgsql.auto重置持久性 启用,每次 PGYP-连接() 正在调用,如果连接链接仍然有效,则检查连接链接。这需要一点开销,但在连接丢失时修复错误消息。

        6
  •  2
  •   Nanang F. Rozi    8 年前

    我也犯了那个错误。我通过重启Web服务器(Apache)来解决我的问题。