Olivier Hoareau nous rappelle de faire attention à la mémoire vive dans les batch.

Je viens juste en remettre un couche par rapport à des petits bugs identifiés récemment dans différentes relecture de vieux code.

boucle environementée

Il ne faut pas voir un batch comme une application, mais comme un environnement à reseter à chaque itérations.

C'est à dire que la consommation de vos ressources doivent être le même en fin de chaque loop. Et il ne faut qu'un loop.

Classiquement on a lecture - traitement - écriture.

Il faut éviter le réflexe de "tout lire" -> boucler sur ce qu'on a lu -> "tout écrire"

Il faut envisager le processus "lecture - traitement - écriture." au niveau de la ligne.

Un exemple, avec un fichier CSV à transformer en tableau HTML.

Premier réflexe, on découpe traitement et affichage.

<?php
 $arrayContenuDuCsv = readCsvFile(); 
 // contient une boucle qui "lit" toutes les lignes
 
$arrayResultat = traitementDesDonnees($arrayContenuDuCsv);
 // contient une boucle effectue les traitements sur chaque ligne
 
 $stringHtml = arrayToHtmlTable($arrayResultat); 
 // contient une boucle effectue les traitements sur chaque ligne
 
 // traitement fini, j'affiche
 echo $stringHtml;

le problème c'est que

<?php
 $arrayContenuDuCsv = readCsvFile(); 
 // contient une boucle qui "lit" toutes les lignes
 
 // ici $arrayContenuDuCsv est de taille non maitrisée, dépendant de la taille du fichier
 
 $arrayResultat = traitementDesDonnees($arrayContenuDuCsv);
 // contient une boucle effectue les traitements sur chaque ligne
 
 // ici $arrayResultat est de taille non maitrisée, dépendant de la taille du fichier
 // pire, comme on a pas encore fait de "unset" de $arrayContenuDuCsv on a le csv en double en mémoire!!!!
 
 $stringHtml = arrayToHtmlTable($arrayResultat); 
 // contient une boucle qui éffectue les traitements sur chaque ligne
 
 
 //ici $stringHtml est de taille non maitrisée, dépendant de la taille du fichier
 // pire, comme on a pas encore fait de "unset" de $arrayResultat on a le csv en double, 
 // et si on a à faire a un très mauvais codeur, il n'a même pas fait de unset($arrayContenuDuCsv)
 // et on a le fichier en triple
 
 // traitement fini, j'affiche
 echo $stringHtml;

Il faut donc voir l'application au niveau du traitement de la ligne

<?php
 $arrayContenuCsv = readCsvRow(); // <- contient une boucle qui "lit" UNE ligne
 
 //ici $arrayContenuCsv est de taille maitrisée, 1 ligne
 
 $arrayResultatLigne = traitementDesDonnees($arrayContenuCsv);
 // contient un traitement identique pour chaque ligne
 
 //ici $arrayResultat est de taille maitrisée, 1 ligne
 
 $stringHtml = arrayToHtmlRowTable($arrayResultat);  //ici $stringHtml est de taille maitrisée, 1 ligne
 // traitement fini de ligne, j'affiche
 echo $stringHtml;

Bien entendu il faut mettre tout ceci dans une boucle, ajouter le header et le footer de ma table, mais ca reste du traitement unique.

Initialiser ses variables.

Voici un autre cas de mauvaise écriture que j'ai rencontré.

[php]
<?php
$arrayData = array();
$arrListedeListes = getListeDeListe();
foreach ($arrListedeListes as $arrListe )
{
     foreach ($arrListe as $ligne)
     {
           $arrayData[]=getResultatDuTraitementDeLaLigne($ligne);
     }
     $result= ecritureDesDatas($arrayData);
}

Le développeur a vu qu'en faisant cela au deuxième passage le traitement des données incluait le nouvelles mais aussi celle de la passe d'avant.

Solution appliquée par le développeur

[php]
<?php
$arrayData = array();
$arrListedeListes = getListeDeListe();
foreach ($arrListedeListes as $arrListe )
{
     $i=0; // <- il a réinitialisé son compteur
     foreach ($arrListe as $ligne)
     {
           $arrayData[$i]=getResultatDuTraitementDeLaLigne($ligne);
           $i++;
     }
     $result= ecritureDesDatas($arrayData);
}

Très mauvaise idée parce qu'en fait ce n'est qu'une version aussi erronée que la précédente mais en masquant un peu le problème.

En effet, en écrasant sans effacer la liste précédente, il se fait que dés qu'une liste était plus courte que la précédente, les lignes en surplus était reconsidérées à nouveau.

[php]
<?php
$arrListedeListes = getListeDeListe();
foreach ($arrListedeListes as $arrListe )
{
     $arrayData = array();

     foreach ($arrListe as $ligne)
     {
           $arrayData[$i]=getResultatDuTraitementDeLaLigne($ligne);
           
     }
     $result= ecritureDesDatas($arrayData);
}

Autres outils

Avec Zend_Framework il existe un outil qui permet de gérer le traitement gourmands en entrée et surtout en sortie c'est Zend_Memory.

Il va permettre de gérer des grosses entités de données en plaçant hors de la mémoire vive ce qu'on n'utilise pas.

Il place l'inutilisé dans un des backends Zend_Cache.

Pensez aussi à lire l'article d'Olivier Hoareau : attention à la mémoire vive.