2009-05-14 8 views
11

J'ai un script de longue date qui semble signaler de temps en temps l'erreur au niveau AVIS suivant: pg_send_query(): Impossible de définir la connexion en mode blocagepg_send_query(): Impossible de définir la connexion en mode bloquant?

Il semble continuer à envoyer des requêtes par la suite, mais on ne sait pas si elle envoie avec succès la requête qui génère l'erreur.

De quoi s'agit-il?

Edit: Il n'y a pas d'entrées dans les postgres connecter au moment de l'erreur, ce qui suggère ceci est uniquement une erreur de connexion, pas quelque chose qui ne va pas du côté de postgres (par exemple, probablement pas le résultat de plantage Postgres et redémarrage ou quelque chose)

Modifier: Autant que je peux dire, mes instructions INSERT réussissent, d'une manière ou d'une autre, lorsque cette erreur est déclenchée.

Edit: On dirait que cela peut avoir été fixé en Juin 2013: https://bugs.php.net/bug.php?id=65015

Répondre

23

Il est un symptôme de pg_send_query() ne pas être en mesure de passer avec succès la connexion au mode de blocage. En regardant le code source dans phps pgsql.c, vous pouvez trouver:

/* {{{ 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; 
} 

Ainsi, l'erreur est soulevée à la fin de la fonction, après le travail principal est fait. Cela correspond à votre observation que vos instructions INSERT sont exécutées. L'objectif des deux appels PQ_SETNONBLOCKING est de mettre la connexion en mode non bloquant pour permettre l'exécution asynchrone et de rétablir ensuite le comportement de blocage par défaut. De l'documentation of PQsetnonblocking: (PQ_SETNONBLOCKING est juste un alias défini pour cette fonction):

Sets the nonblocking status of the connection.

int PQsetnonblocking(PGconn *conn, int arg); 

Sets the state of the connection to nonblocking if arg is 1, or blocking if arg is 0. Returns 0 if OK, -1 if error.

In the nonblocking state, calls to PQsendQuery, PQputline, PQputnbytes, and PQendcopy will not block but instead return an error if they need to be called again.

Note that PQexec does not honor nonblocking mode; if it is called, it will act in blocking fashion anyway.

En regardant plus à la source de PQsetnonblocking (en PostgeSQLs fe-exec.c), il y a deux raisons possibles pour lesquelles l'appel pourrait échouer :

/* 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; 
} 

donc, soit la connexion se sont perdus en quelque sorte, ou PQflush n'a pas fini avec succès, ce qui indique des choses qui reste dans la mémoire tampon de sortie de connexion.

Le premier cas serait inoffensif, car votre script remarquerait certainement la perte de connexion pour les appels ultérieurs et réagirait à cela (ou échouerait plus perceptible).Ceci laisse le deuxième cas, ce qui signifie que vous avez une connexion à l'état non par défaut, non bloquant. Je ne sais pas si cela pourrait affecter les appels ultérieurs qui réutiliseraient cette connexion. Si vous voulez jouer en toute sécurité, vous fermez la connexion dans ce cas et utilisez un nouveau/autre.

+5

+1 pour aller directement à la source –

+4

+1 pour identifier les causes réelles et faire des suggestions pratiques sur ce qu'il faut faire. Bien joué! –

+1

Je pense que j'ai malheureusement réussi à répéter cette erreur avec pg_query() combiné avec pg_pconnect(). L'utilisation de pg_send_query() n'est pas requise. La requête pg_query semble générer aléatoirement cette erreur si la connexion précédente a été automatiquement annulée lorsque la nouvelle connexion est acquise avec pg_pconnect(). Problème était visible avec PHP 5.3.2 et Postgres 8.4 fonctionnant sur Ubuntu 10.04 LTS x86-64. –

3

Il semble que vous essayez d'utiliser la fonction pg_send_query() pour envoyer des requêtes asynchrones à PostgreSQL. Le but de cette fonction est de permettre à votre script PHP de continuer à exécuter d'autres codes en attendant que PostgreSQL exécute votre requête et prépare un résultat.

L'exemple donné dans le docs pour pg_send_query() suggèrent que vous ne devriez pas envoyer une requête si PostgreSQL est déjà à mâcher sur une autre requête:

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

Y at-il une raison que vous utilisez pg_send_query() au lieu de pg_query()? Si vous pouvez autoriser votre script à bloquer l'attente de l'exécution de la requête, je devine (certes, sans l'avoir essayé) que vous ne verrez pas ces erreurs.

+1

+1 - J'ai supposé qu'il le faisait de façon asynchrone, étant donné la remarque 'long-running script', mais ce serait la solution évidente si ce n'est pas le cas :) –

+1

Il faut utiliser 'pg_send_query () 'pour pouvoir interroger correctement les erreurs en cas d'échec de la requête. Sinon, vous devez utiliser 'pg_last_error()' qui ne fonctionne pas vraiment bien. –

+1

Attention au bug https://bugs.php.net/bug.php?id=65015 si vous utilisez 'pg_send_query()' et parlez au serveur distant.Si la chaîne de requête est trop longue (en octets), le 'pg_send_query()' va échouer à cause du bug de mise en oeuvre (flush manquant avant de toucher counnection). Pour solution de contournement, installez PgBouncer et utilisez pg_connect() pour vous connecter à localhost PgBouncer au lieu de vous connecter directement au serveur distant. –

2

Cela peut se produire si vous utilisez threads et que la connexion est en cours de réutilisation. Si est-ce le cas, vous pouvez utiliser le PGSQL_CONNECT_FORCE_NEW comme ceci:

pg_connect("...", PGSQL_CONNECT_FORCE_NEW) 

Cela forcera une nouvelle ressource de connexion de base de données, mais être conseillé: vous pourriez manquer de connexions clients, donc être soigneusement à l'aide ce fils à l'intérieur alors n'oubliez pas d'utiliser pg_close().

3

J'ai récemment eu le même problème, et avec l'aide d'Henrik Opels, la réponse a réalisé que PHP n'attendait pas que le tampon soit vidé avant de remettre la connexion en mode de blocage. La 'connexion impossible au mode de blocage' est trivialement répétable avec suffisamment de requêtes pour remplir le tampon d'envoi (un remplissage avec des espaces à la fin suffit). Avec de plus petites requêtes j'imagine que cela dépend de la charge, et plutôt intermittent.

si vous avez besoin en fait le mode asynchrone puis essayez le patch à https://bugs.php.net/bug.php?id=65015

+0

On peut aussi installer PgBouncer sur localhost et utilisez le mode asynchrone via celui-ci. Le bug PHP n'est déclenché que si l'hôte est distant. –

+0

J'ai aussi ce problème sur les connexions localhost. Voulez-vous dire que le problème disparaît si nous utilisons des connexions socket? –

1

Je rencontrais même message d'erreur avec PHP 5.6.9

Il se produit lorsque la connexion persistante faite par pg_pconnect() est perdu et pgsql.auto_reset_persistent est défini sur Désactivé.

Connection peut se perdre lorsque:

  1. PHP session expire
  2. Connexion à DB temps morts
  3. Webserver/serveur DB est redémarré

Vous pouvez vérifier php.ini pgsql .auto_reset_persistent et réglez-le sur sur.

Avec pgsql.auto_reset_persistent activé, chaque fois pg_pconnect() est appelé, un lien de connexion est cochée, si elle est toujours valide. Cela nécessite un léger surcoût, mais un message d'erreur fixe lorsque la conncetion est perdue.

2

J'ai aussi cette erreur. Je résous mon problème en redémarrant le serveur web (Apache).

Questions connexes