Re: php/fastcgi upload crash

17 views
Skip to first unread message

Stéphane Gully

unread,
Jul 25, 2010, 3:29:05 PM7/25/10
to pk...@googlegroups.com
Pas vraiment de solution pour le moment mais un lien : http://php-fpm.org/
C'est un candidat pour remplacer PHP FastCGI.

Les fonctionnalités annoncées sont sympa :
  • Adaptive process spawning (NEW!)
  • Basic statistics (ala Apache's mod_status) (NEW!)
  • Advanced process management with graceful stop/start
  • Ability to start workers with different uid/gid/chroot/environment and different php.ini (replaces safe_mode)
  • Stdout & stderr logging
  • Emergency restart in case of accidental opcode cache destruction
  • Accelerated upload support
  • Support for a "slowlog"
  • Enhancements to FastCGI, such as fastcgi_finish_request() - a special function to finish request & flush all data while continuing to do something time-consuming (video converting, stats processing, etc.)

Stéphane


On Fri, Jul 23, 2010 at 11:09 PM, Nicolas Thouvenin <nthou...@gmail.com> wrote:
>
> hello pkgi's users
>
> Je crois que j'ai trouvé un moyen de cracher votre demon fastcgi/php
>
> Constatez-vous le même problème ?
> Avez-vous une solution ?
>
> 1. Tout d'abord, il vous faut un petit script php, que l'on appellera "put.php"
> -------------- put.php ---------------------------8<---------------------------
> <?php
> echo 'OK';
> $handle = fopen("php://input", "rb");
> while (!feof($handle)) {
>    fread($handle, 8192);
> }
> fclose($handle);
> ?>
> ------------------------------------------------------>8---------------------------
>
>
> 2. Ensuite 3 fichiers avec 3 tailles différentes :  255Mo, 256Mo & 257Mo
> Vous pouvez les obtenir via les commandes suivantes :
> dd if=/dev/zero of=255Mo.txt bs=1k count=261120
> dd if=/dev/zero of=256Mo.txt bs=1k count=262144
> dd if=/dev/zero of=257Mo.txt bs=1k count=263168
>
> 4. Puis une configuration php légèrement gonflée :
> --------------- php.ini ---------------------------8<---------------------------
> file_uploads = On
> memory_limit = 512M
> upload_max_filesize = 512M
> post_max_size = 512M
> ------------------------------------------------------>8---------------------------
>
>
> 3. Enfin, il vous suffit d'envoyer le fichier le plus gros au script php.
> pour obtenir une erreur 500 Internal Error
>
> Je crois même que le crash est reproductible avec une configuration
> par défaut et des fichiers autour de 8Mo + ou - quelques octets
>
>
> 4. Résultats
>
> ------ 255Mo ------------
> $ curl http://127.0.0.1:8000/put.php --data-binary @./255M.txt
> OK
>
> ------ 256Mo ------------
> $ curl http://127.0.0.1:8000/put.php --data-binary @./256M.txt
> <br />
> <font size='1'><table dir='ltr' border='1' cellspacing='0' cellpadding='1'>
> <tr><th align='left' bgcolor='#f57900' colspan="5"><span
> style='background-color: #cc0000; color: #fce94f; font-size:
> x-large;'>( ! )</span> Fatal error: Allowed memory size of 536870912
> bytes exhausted (tried to allocate 268435457 bytes) in Unknown on line
> <i>0</i></th></tr>
>
>
>
> ------ 257Mo ------------
> $ curl http://127.0.0.1:8000/put.php --data-binary @./257M.txt
> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
> <html><head>
> <title>500 Internal Server Error</title>
> </head><body>
> <h1>Internal Server Error</h1>
> <p>The server encountered an internal error or
> misconfiguration and was unable to complete
> your request.</p>
> <p>Please contact the server administrator,
>  and inform them of the time the error occurred,
> and anything you might have done that may have
> caused the error.</p>
> <p>More information about this error may be available
> in the server error log.</p>
> <hr>
> <address>Apache/2.2.12 (Ubuntu) Server at 127.0.0.1 Port 8000</address>
> </body></html>
>
>
>
> Annexes :
>
> les logs
>
> ------ 255Mo ------------
> [Fri Jul 23 16:47:02 2010] [info] mod_fcgid: server
> /home/thouveni/applis/userapp/usr/lib/fcgi-bin/php-fcgi-wrapper(21344)
> started
>
>
> ------ 256Mo ------------
> [23-Jul-2010 16:47:17] PHP Fatal error:  Allowed memory size of
> 536870912 bytes exhausted (tried to allocate 268435457 bytes) in
> Unknown on line 0
>
> ------ 257Mo ------------
> [Fri Jul 23 16:47:26 2010] [notice] mod_fcgid: process
> /home/thouveni/applis/userapp/usr/lib/fcgi-bin/php-fcgi-wrapper(21344)
> exit(normal exit), terminated by calling exit(), return code: 255
> [Fri Jul 23 16:47:26 2010] [warn] mod_fcgid: cleanup zombie process 21344
> [Fri Jul 23 16:47:30 2010] [info] mod_fcgid: server
> /home/thouveni/applis/userapp/usr/lib/fcgi-bin/php-fcgi-wrapper(21352)
> started
> [Fri Jul 23 16:47:31 2010] [warn] (104)Connection reset by peer:
> mod_fcgid: read data from fastcgi server error.
> [Fri Jul 23 16:47:31 2010] [error] [client 127.0.0.1] Premature end of
> script headers: php-fcgi-wrapper
> [Fri Jul 23 16:47:36 2010] [notice] mod_fcgid: process
> /home/thouveni/applis/userapp/usr/lib/fcgi-bin/php-fcgi-wrapper(21352)
> exit(communication error), get unexpected signal 11

Nicolas Thouvenin

unread,
Jul 26, 2010, 3:26:45 AM7/26/10
to pk...@googlegroups.com
J'ai trouvé, le problème s'appelle Xdebug.

En désactivant xdebug, le fichier de 257M.txt ne provoque plus de
crash mais affiche le même message d'erreur qu'avec le fichier de
256M.

D'autre part, ce message nous indique que PHP bufferise l'entrée
standard. Il me semble donc judicieux de fixer la limite mémoire
suivant cette régle simple :

memory_limit = (post_max_size * 2) + 1

soit dans mon cas :

memory_limit = 513M
post_max_size = 256M

Nicolas Thouvenin

unread,
Jul 26, 2010, 3:26:51 AM7/26/10
to pk...@googlegroups.com
Effectivement sur le papier ça semble super intéressant ...

affaire à suivre

Stéphane Gully

unread,
Jul 26, 2010, 12:37:12 PM7/26/10
to pk...@googlegroups.com
Pour info, j'ai modifié quelques paramètres pour désactiver la bufferisation. Je pense d'ailleurs intégrer la non bufferisation en standard dans pkgi car cela permet d'afficher ce qui se passe en live et également d'éviter de se planter avec des timeout (FcgidIOTimeout ou IPCCommTimeout) lorsque l'on exécute de longs scripts via le Web.

Voila les tests réalisés :

-> put.php (en vert pour envoyer des données pendant la requête)
<?php
echo "start\n";
$handle = fopen("php://input", "rb");
while (!feof($handle)) {
   fread($handle, 8192);
   echo '.';
   flush();
   sleep(1);
}
fclose($handle);
echo "stop\n";


-> pkgi/php/etc/apache2/conf.d/php (en vert les options importantes)

# Load fcgi module
Include /etc/apache2/mods-available/fcgid.load
Include /etc/apache2/mods-available/fcgid.conf

<IfModule mod_fcgid.c>
    SharememPath <?php echo getenv('APPNAME_HOME') ?>/var/run/fcgid_shm
    SocketPath <?php echo getenv('APPNAME_HOME') ?>/var/lib/apache2/fcgid/

    # Maximum requests before a process is stopped and a new one is launched
    FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 5000
    DefaultInitEnv  PHP_FCGI_MAX_REQUESTS 5000
    
    # Number of PHP childs that will be launched. Leave undefined to let PHP decide.
    FcgidInitialEnv PHP_FCGI_CHILDREN 5
    DefaultInitEnv  PHP_FCGI_CHILDREN 5

    # Disable fcgid output buffer
    FcgidOutputBufferSize 0
    OutputBufferSize 0

    # Communication timeout: Default value is 40 seconds
    FcgidIOTimeout 60
    IPCCommTimeout 60
    
    # Connection timeout: Default value is 3 seconds
    #FcgidConnectTimeout 3
    #IPCConnectTimeout   3
    
    # Define a new handler "php-fcgi" for ".php" files, plus the action that must follow
    AddHandler php-fcgi .php
    Action php-fcgi /fcgi-bin/php-fcgi-wrapper

    # Define the MIME-Type for ".php" files
    AddType application/x-httpd-php .php

    # Define alias "/fcgi-bin/". The action above is using this value, which means that
    # you could run another "php5-cgi" command by just changing this alias
    Alias /fcgi-bin/ <?php echo getenv('APPNAME_HOME') ?>/usr/lib/fcgi-bin/

    # Turn on the fcgid-script handler for all files within the alias "/fcgi-bin/"
    <Location /fcgi-bin/>
        Order Deny,Allow
        Deny from All
        Allow from env=REDIRECT_STATUS
        SetHandler fcgid-script
        Options +ExecCGI 
    </Location>
</IfModule>


J'exécute avec l'option --no-buffer de cette façon :
curl http://127.0.0.1:34578/put.php --no-buffer --data-binary @./257Mo.txt

Et la désactivation du buffer fonctionne, des "......" s'affichent en live.

Nicolas Thouvenin

unread,
Jul 27, 2010, 2:22:43 AM7/27/10
to pk...@googlegroups.com
super, un point bloquant de moins pour fastcgi, mais rassures moi
l'instruction sleep(1) c'est esthétique ?

Sinon, je suis revenu au mod_php, car après avoir franchi le cas de la
réception des données
ça explose ailleurs. En mode fastcgi, php semble vraiment très
instable avec des volumes de données
de plusieurs centaines de Mo

Stéphane Gully

unread,
Jul 27, 2010, 3:36:42 AM7/27/10
to pk...@googlegroups.com
Oui c'est esthétique, ça me permet de voir que les données n'arrivent pas toutes à la fois sinon mon terminal se brouille de milliers de points en une nano seconde  :-)

Stéphane


2010/7/27 Nicolas Thouvenin <nthou...@gmail.com>

Stéphane Gully

unread,
Jul 27, 2010, 9:23:19 AM7/27/10
to pk...@googlegroups.com
Mauvaise nouvelle, mon test était biaisé. En réalité les options FcgidOutputBufferSize et OutputBufferSize permettent de régler la taille d'un seul des deux buffers, B2 sur ce schéma :
buffer.png
Il existe malheureusement également un buffer en entrée que j'ai noté B1. La requêtes (1) va donc être totalement transmise et stoquée dans B1 avant d'être propagée (2) au script PHP. Ensuite les multiples flush() vont permettre de propager (3) l'état d'avancement du script au client. Et c'est à ce niveau que le second buffer (B2) va intercepter les résultats du script PHP avant de les propager au client (4). Ce dernier buffer est lui paramétrable (et même désactivable) via l'option FcgidOutputBufferSize (nouveau nom de OutputBufferSize).

Pour permettre un streaming complet entre le client et le script PHP il faut relever le défi de désactiver le buffer B1. J'ai pas trouvé pour le moment...

Stéphane


2010/7/27 Stéphane Gully <stephan...@gmail.com>
buffer.png

Stéphane Gully

unread,
Jul 27, 2010, 9:32:57 AM7/27/10
to pk...@googlegroups.com
Bon en 2007 y a une personne qui avait fait la demande pour désactiver le "input buffering" :
Malheureusement sa demande n'a pas donné de suite j'ai l'impression.
buffer.png

Stéphane Gully

unread,
Jul 27, 2010, 9:50:35 AM7/27/10
to pk...@googlegroups.com
Un autre projet à suivre : mod_proxy_fcgi (déjà intégré officiellement à apache)

J'aime bien les objectifs annoncés du projet :
"My goal is to implement a suitable implementation of FastCGI (who work correctly with PHP!) with fully open source licence"
buffer.png

Stéphane Gully

unread,
Jul 27, 2010, 11:42:51 AM7/27/10
to pk...@googlegroups.com
Pour info, voici les variables fcgid qui offrent le meilleur compromis en performance avec et sans APC :
    DefaultMaxClassProcessCount 16
    FcgidMaxProcessesPerClass   16
    DefaultMinClassProcessCount 2
    FcgidMinProcessesPerClass   2
    FcgidInitialEnv PHP_FCGI_CHILDREN 0
    DefaultInitEnv  PHP_FCGI_CHILDREN 0

Observations faites avec le programme siege mais à valider sur un site en production.
buffer.png

Stéphane Gully

unread,
Jul 27, 2010, 1:08:19 PM7/27/10
to pk...@googlegroups.com
La version 2.27 intègre toutes ces dernières optimisations.
Il ne reste plus qu'a les tester en production.

buffer.png

Nicolas Thouvenin

unread,
Jul 28, 2010, 2:29:29 AM7/28/10
to pk...@googlegroups.com
très bonne investigation.

Petite précision, le schéma laisse à penser que B1 et B2 sont identiques.
Mais ça ne semble pas être le cas, B2 est une temporistion d'écriture traditionnelle, elle permet d'envoyer des données par paquets d'octets.  Tout comme on lit un buffer par paquet (cf. fread).
B1 me fait penser à un chargement d'une structure en mémoire et il serait donc impossible à désactiver.

Maintenant, qui de PHP ou de fastcgi est responsable ?
Le programme Perl, ci-après, me fait dire que le comportement n'est pas identique avec Perl & Fastcgi et que c'est donc l'association PHP & Fastcgi qui n'est pas optimisée

#!/usr/bin/perl
use FCGI;
$count = 0;

REQUEST:
while(FCGI::accept() >= 0)  {
    print("Content-type: text/html\r\n\r\n",
          "<title>FastCGI (Perl)</title>\n",
          "<h1>FastCGI  (Perl)</h1>\n",
          "Request number ",  ++$count
          );
          read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
          #print($buffer);
}



010/7/27 Stéphane Gully <stephan...@gmail.com>
buffer.png

Stéphane Gully

unread,
Jul 28, 2010, 3:36:06 AM7/28/10
to pk...@googlegroups.com
Attention car mod_fcgid  et mod_fastcgi ce n'est pas la même chose.

Nous utilisons actuellement fcgid dans pkgi et c'est sur ce module que nous avons rencontré ces problèmes. Rien ne dit qu'avec mod_fastcgi nous allons rencontrer les mêmes problèmes car ce ne sont pas les même logiciels.
Dans ton test avec perl, tu passes par fcgid ou fastcgi ? Vu les nom des variables, j'ai l'impression que tu passes par fastcgi.

Stéphane


2010/7/28 Nicolas Thouvenin <nthou...@gmail.com>
buffer.png

Nicolas Thouvenin

unread,
Jul 28, 2010, 9:16:22 AM7/28/10
to pk...@googlegroups.com
J'utilise bien fcgid

pour cela :
- j'ai placé mon script dans le répertoire usr/lib/fcgi-bin/
- j'ai modifié la configuration apache / pkgi comme ceci
  <Location /fcgi-bin/>
        Order Deny,Allow
        Deny from none
        Allow from all

        SetHandler fcgid-script
        Options +ExecCGI
    </Location>

et il reste à appeler le script via cette url /fcgi-bin/tiny-fcgi




2010/7/28 Stéphane Gully <stephan...@gmail.com>
buffer.png

Stéphane Gully

unread,
Jul 29, 2010, 3:24:09 AM7/29/10
to pk...@googlegroups.com
Pour info, d'après ce post, la règle à suivre pour le réglage de memory_limit est différente.
<< The PHP documentation recomends memory_limit larger than post_max_size, so as a rule of thumb starting with 16 + post_max_size (16M is the default PHP value) should be enough. >>

Ca donne donc ça  :
memory_limit = post_max_size + 16

Stéphane


2010/7/26 Nicolas Thouvenin <nthou...@gmail.com>
Reply all
Reply to author
Forward
0 new messages